@arkade-os/swap 0.0.14 → 0.0.16
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 +71 -8
- package/dist/{chunk-IFZOBTTG.js → chunk-7N7XO4PY.js} +82 -3
- package/dist/index.cjs +808 -306
- package/dist/index.d.cts +192 -9
- package/dist/index.d.ts +192 -9
- package/dist/index.js +624 -207
- package/dist/nostr.cjs +80 -3
- package/dist/nostr.d.cts +1 -1
- package/dist/nostr.d.ts +1 -1
- package/dist/nostr.js +1 -1
- package/dist/repositories/realm/index.d.cts +2 -2
- package/dist/repositories/realm/index.d.ts +2 -2
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/{repository-D5JoeqwB.d.ts → repository-DH5zjdw4.d.ts} +1 -1
- package/dist/{repository-Vfx65uF1.d.cts → repository-rwU1Sv2-.d.cts} +1 -1
- package/dist/{rfq-CYfKvAPp.d.ts → rfq-DirJn_4k.d.cts} +20 -2
- package/dist/{rfq-CYfKvAPp.d.cts → rfq-DirJn_4k.d.ts} +20 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ARKADE_ASSET: () => ARKADE_ASSET,
|
|
34
34
|
ARKADE_BTC: () => ARKADE_BTC,
|
|
35
|
+
ASSET_CARRIER_SATS: () => ASSET_CARRIER_SATS,
|
|
35
36
|
AddressMismatch: () => AddressMismatch,
|
|
36
37
|
BTC_ASSET_ID: () => BTC_ASSET_ID,
|
|
37
38
|
CROSS_ASSET_RAIL: () => CROSS_ASSET_RAIL,
|
|
@@ -46,6 +47,7 @@ __export(index_exports, {
|
|
|
46
47
|
LockupContractMissing: () => LockupContractMissing,
|
|
47
48
|
LockupNeedsRecoveryError: () => LockupNeedsRecoveryError,
|
|
48
49
|
LockupRegistrationFailed: () => LockupRegistrationFailed,
|
|
50
|
+
MARKET_CORRIDORS: () => MARKET_CORRIDORS,
|
|
49
51
|
MAX_MIN_CONFIRMATIONS: () => MAX_MIN_CONFIRMATIONS,
|
|
50
52
|
MIN_CLAIM_WINDOW_SECONDS: () => MIN_CLAIM_WINDOW_SECONDS,
|
|
51
53
|
MIN_HEADROOM_SECONDS: () => MIN_HEADROOM_SECONDS,
|
|
@@ -60,6 +62,7 @@ __export(index_exports, {
|
|
|
60
62
|
PreimageNotRecoverableError: () => PreimageNotRecoverableError,
|
|
61
63
|
QUOTE_OPTIONS: () => QUOTE_OPTIONS,
|
|
62
64
|
REFUND_MTP_LAG_SECONDS: () => REFUND_MTP_LAG_SECONDS,
|
|
65
|
+
RFQ_REFUSAL_ERROR_CODES: () => RFQ_REFUSAL_ERROR_CODES,
|
|
63
66
|
RFQ_RESOLVED_STATES: () => RFQ_RESOLVED_STATES,
|
|
64
67
|
RFQ_SWAP_RETENTION_SECONDS: () => RFQ_SWAP_RETENTION_SECONDS,
|
|
65
68
|
RFQ_SWAP_TERMINAL_STATES: () => RFQ_SWAP_TERMINAL_STATES,
|
|
@@ -103,11 +106,14 @@ __export(index_exports, {
|
|
|
103
106
|
discoverMarkets: () => discoverMarkets,
|
|
104
107
|
encodeOffer: () => encodeOffer,
|
|
105
108
|
extractPreimage: () => extractPreimage,
|
|
109
|
+
fillOffer: () => fillOffer,
|
|
106
110
|
findLockupVtxos: () => findLockupVtxos,
|
|
107
111
|
findMarket: () => findMarket,
|
|
108
112
|
getAssetSwaps: () => getAssetSwaps,
|
|
109
113
|
getAssetSwapsOrThrow: () => getAssetSwapsOrThrow,
|
|
110
114
|
httpTransport: () => httpTransport,
|
|
115
|
+
isRfqMarket: () => isRfqMarket,
|
|
116
|
+
isRfqRefusalErrorCode: () => isRfqRefusalErrorCode,
|
|
111
117
|
isRfqSwapTerminal: () => isRfqSwapTerminal,
|
|
112
118
|
isRfqTerminal: () => isRfqTerminal,
|
|
113
119
|
l1ScriptForAddress: () => l1ScriptForAddress,
|
|
@@ -116,6 +122,9 @@ __export(index_exports, {
|
|
|
116
122
|
lightningSendVtxoScript: () => lightningSendVtxoScript,
|
|
117
123
|
lockupContractParams: () => lockupContractParams,
|
|
118
124
|
makeCachedFeedFetch: () => makeCachedFeedFetch,
|
|
125
|
+
marketAssetId: () => marketAssetId,
|
|
126
|
+
marketCorridor: () => marketCorridor,
|
|
127
|
+
marketPairLabel: () => marketPairLabel,
|
|
119
128
|
newPreimage: () => newPreimage,
|
|
120
129
|
newRfqId: () => newRfqId,
|
|
121
130
|
nextOnchainAction: () => nextOnchainAction,
|
|
@@ -133,13 +142,16 @@ __export(index_exports, {
|
|
|
133
142
|
rebuildRfqSwap: () => rebuildRfqSwap,
|
|
134
143
|
receiveVtxoScript: () => receiveVtxoScript,
|
|
135
144
|
refundIfUnresolved: () => refundIfUnresolved,
|
|
145
|
+
registerAssetSwapRestore: () => registerAssetSwapRestore,
|
|
136
146
|
registerLockupContract: () => registerLockupContract,
|
|
137
147
|
relayTransport: () => relayTransport,
|
|
138
148
|
requestLightningReceive: () => requestLightningReceive,
|
|
139
149
|
requestLightningSend: () => requestLightningSend,
|
|
140
150
|
requestOnchainReceive: () => requestOnchainReceive,
|
|
141
151
|
requestOnchainSend: () => requestOnchainSend,
|
|
152
|
+
restoreAssetSwapRepository: () => restoreAssetSwapRepository,
|
|
142
153
|
restoreAssetSwaps: () => restoreAssetSwaps,
|
|
154
|
+
restoreOfferCoverage: () => restoreOfferCoverage,
|
|
143
155
|
retireSettledOfferContracts: () => retireSettledOfferContracts,
|
|
144
156
|
revealClaimPacket: () => revealClaimPacket,
|
|
145
157
|
revealFieldsFromScript: () => revealFieldsFromScript,
|
|
@@ -283,10 +295,10 @@ function addressOutstanding(swaps, script) {
|
|
|
283
295
|
}
|
|
284
296
|
return true;
|
|
285
297
|
}
|
|
286
|
-
async function promoteOfferContract(manager, script) {
|
|
298
|
+
async function promoteOfferContract(manager, script, issued = Date.now()) {
|
|
287
299
|
await serialize(script, async () => {
|
|
288
300
|
await manager.setContractWatchState(script, "watched");
|
|
289
|
-
issuedAt.set(script,
|
|
301
|
+
issuedAt.set(script, issued);
|
|
290
302
|
});
|
|
291
303
|
}
|
|
292
304
|
async function retireOfferContract(manager, swaps, script) {
|
|
@@ -492,7 +504,7 @@ function u64(name, value) {
|
|
|
492
504
|
if (value < BigInt(0) || value >> BigInt(64) > BigInt(0)) {
|
|
493
505
|
throw new Error(`${name} does not fit the offer wire format (u64)`);
|
|
494
506
|
}
|
|
495
|
-
const out = new Uint8Array(
|
|
507
|
+
const out = new Uint8Array(8);
|
|
496
508
|
new DataView(out.buffer).setBigUint64(0, value, false);
|
|
497
509
|
return out;
|
|
498
510
|
}
|
|
@@ -627,10 +639,9 @@ function decodeExitDelay(value) {
|
|
|
627
639
|
}
|
|
628
640
|
var OFFER_CONTRACT_LABEL = "Arkade swap offer";
|
|
629
641
|
var OFFER_CONTRACT_KIND = "asset-swap-offer";
|
|
630
|
-
async function registerOfferContract(wallet, arkServerUrl, network, binding, serverPubkey, expectedPkScript) {
|
|
631
|
-
const
|
|
632
|
-
const
|
|
633
|
-
const client = await import_sdk2.arkade.Arkade.connect({
|
|
642
|
+
async function registerOfferContract(wallet, arkServerUrl, network, binding, serverPubkey, expectedPkScript, opts = {}) {
|
|
643
|
+
const contractManager = opts.contractManager ?? await wallet.getContractManager();
|
|
644
|
+
const client = opts.client ?? await import_sdk2.arkade.Arkade.connect({
|
|
634
645
|
arkade: new import_sdk2.RestArkProvider(arkServerUrl),
|
|
635
646
|
indexer: new import_sdk2.RestIndexerProvider(arkServerUrl),
|
|
636
647
|
identity: wallet.identity,
|
|
@@ -640,15 +651,66 @@ async function registerOfferContract(wallet, arkServerUrl, network, binding, ser
|
|
|
640
651
|
network: (0, import_sdk2.getNetwork)(network),
|
|
641
652
|
contractManager
|
|
642
653
|
});
|
|
654
|
+
await contractManager.createContract(
|
|
655
|
+
offerContractParams(client, binding, serverPubkey, expectedPkScript)
|
|
656
|
+
);
|
|
657
|
+
await promoteOfferContract(contractManager, import_base2.hex.encode(expectedPkScript), opts.issued);
|
|
658
|
+
}
|
|
659
|
+
function offerContractParams(client, binding, serverPubkey, expectedPkScript) {
|
|
660
|
+
const { program, args, keys } = swapProgramBinding(binding, serverPubkey);
|
|
643
661
|
const contract = new import_sdk2.arkade.ArkadeContract(client, program, args, keys);
|
|
644
662
|
if (import_base2.hex.encode(contract.pkScript) !== import_base2.hex.encode(expectedPkScript)) {
|
|
645
663
|
throw new Error("derived covenant does not match the offer's swapPkScript");
|
|
646
664
|
}
|
|
647
|
-
|
|
665
|
+
return {
|
|
666
|
+
...contract.toContractParams(),
|
|
648
667
|
label: OFFER_CONTRACT_LABEL,
|
|
649
668
|
metadata: { genericallySpendable: false, kind: OFFER_CONTRACT_KIND }
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
async function restoreOfferCoverage(wallet, arkServerUrl, swaps) {
|
|
672
|
+
const live = swaps.filter((s) => !RETIRABLE.includes(s.status));
|
|
673
|
+
if (live.length === 0) return;
|
|
674
|
+
const arkProvider = new import_sdk2.RestArkProvider(arkServerUrl);
|
|
675
|
+
const info = await arkProvider.getInfo();
|
|
676
|
+
const serverPubKey = import_base2.hex.decode((0, import_sdk2.toXOnlySignerHex)(info.signerPubkey));
|
|
677
|
+
const contractManager = await wallet.getContractManager();
|
|
678
|
+
const client = await import_sdk2.arkade.Arkade.connect({
|
|
679
|
+
arkade: arkProvider,
|
|
680
|
+
indexer: new import_sdk2.RestIndexerProvider(arkServerUrl),
|
|
681
|
+
identity: wallet.identity,
|
|
682
|
+
network: (0, import_sdk2.getNetwork)(info.network),
|
|
683
|
+
contractManager
|
|
650
684
|
});
|
|
651
|
-
|
|
685
|
+
const done = /* @__PURE__ */ new Set();
|
|
686
|
+
const covenants = [];
|
|
687
|
+
for (const swap of live) {
|
|
688
|
+
if (done.has(swap.swapPkScript)) continue;
|
|
689
|
+
done.add(swap.swapPkScript);
|
|
690
|
+
try {
|
|
691
|
+
const { swapPkScript: _, ...binding } = decodeOffer(import_base2.hex.decode(swap.offerHex));
|
|
692
|
+
covenants.push({
|
|
693
|
+
script: swap.swapPkScript,
|
|
694
|
+
issued: swap.createdAt,
|
|
695
|
+
params: offerContractParams(
|
|
696
|
+
client,
|
|
697
|
+
binding,
|
|
698
|
+
serverPubKey,
|
|
699
|
+
import_base2.hex.decode(swap.swapPkScript)
|
|
700
|
+
)
|
|
701
|
+
});
|
|
702
|
+
} catch (err) {
|
|
703
|
+
console.warn(`[swap] could not restore coverage for ${swap.swapPkScript}`, err);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (covenants.length === 0) return;
|
|
707
|
+
const paramsList = covenants.map((c) => c.params);
|
|
708
|
+
if (contractManager.createContracts) {
|
|
709
|
+
await contractManager.createContracts(paramsList);
|
|
710
|
+
} else {
|
|
711
|
+
for (const params of paramsList) await contractManager.createContract(params);
|
|
712
|
+
}
|
|
713
|
+
for (const c of covenants) await promoteOfferContract(contractManager, c.script, c.issued);
|
|
652
714
|
}
|
|
653
715
|
function serverExitDelay(delay) {
|
|
654
716
|
if (typeof delay !== "bigint" || delay <= BigInt(0)) {
|
|
@@ -761,10 +823,170 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
|
761
823
|
}
|
|
762
824
|
return txid;
|
|
763
825
|
}
|
|
826
|
+
var ASSET_CARRIER_SATS = BigInt(import_sdk2.ASSET_CARRIER_SATS);
|
|
827
|
+
async function fillOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
828
|
+
const {
|
|
829
|
+
fund,
|
|
830
|
+
payoutScript,
|
|
831
|
+
fundingTxid,
|
|
832
|
+
swapAddress,
|
|
833
|
+
assetCarrierSats = ASSET_CARRIER_SATS,
|
|
834
|
+
emulator,
|
|
835
|
+
emulatorPubkey
|
|
836
|
+
} = opts;
|
|
837
|
+
const offer = decodeOffer(import_base2.hex.decode(offerHex));
|
|
838
|
+
const wantedAssetId = offer.wantAsset?.toString();
|
|
839
|
+
if (fund.length === 0) {
|
|
840
|
+
throw new Error("fillOffer needs coins to pay wantAmount with \u2014 `fund` is empty");
|
|
841
|
+
}
|
|
842
|
+
if (wantedAssetId !== void 0) {
|
|
843
|
+
const supplied = fund.reduce(
|
|
844
|
+
(sum, coin) => sum + amountOfAsset(coin.assets, wantedAssetId),
|
|
845
|
+
BigInt(0)
|
|
846
|
+
);
|
|
847
|
+
if (supplied < offer.wantAmount) {
|
|
848
|
+
throw new Error(
|
|
849
|
+
`fillOffer needs ${offer.wantAmount} of ${wantedAssetId} to pay the maker, but \`fund\` declares ${supplied} \u2014 pass coins carrying it, and declare their assets`
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const contractManager = await wallet.getContractManager();
|
|
854
|
+
const client = await import_sdk2.arkade.Arkade.connect({
|
|
855
|
+
arkade: new import_sdk2.RestArkProvider(arkServerUrl),
|
|
856
|
+
indexer: new import_sdk2.RestIndexerProvider(arkServerUrl),
|
|
857
|
+
identity: wallet.identity,
|
|
858
|
+
contractManager,
|
|
859
|
+
// `fulfill` is a covenant path, so the emulator executes the arkade
|
|
860
|
+
// script and finalizes with arkd. Without it the spend is built and then
|
|
861
|
+
// refused at submission — there is no default to fall back on.
|
|
862
|
+
emulator: typeof emulator === "string" ? new import_sdk2.RestEmulatorProvider(emulator) : emulator,
|
|
863
|
+
// Only reached for the client's own emulatorKey, which this fill never
|
|
864
|
+
// derives against — the offer's own key is bound below. It still has to
|
|
865
|
+
// resolve, and on a network with no pinned key it throws without this.
|
|
866
|
+
...emulatorPubkey ? { emulatorPubkey } : {}
|
|
867
|
+
});
|
|
868
|
+
const serverKey = swapAddress ? import_sdk2.ArkAddress.decode(swapAddress).serverPubKey : client.serverKey;
|
|
869
|
+
const { program, args, keys } = swapProgramBinding(offer, serverKey);
|
|
870
|
+
const rebuilt = new import_sdk2.arkade.ArkadeProgramScript(program, args, keys);
|
|
871
|
+
if (import_base2.hex.encode(rebuilt.pkScript) !== import_base2.hex.encode(offer.swapPkScript)) {
|
|
872
|
+
throw new Error(
|
|
873
|
+
"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"
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
const contract = new import_sdk2.arkade.ArkadeContract(client, program, args, keys);
|
|
877
|
+
const [vtxos, takerAddress] = await Promise.all([contract.getUtxos(), wallet.getAddress()]);
|
|
878
|
+
if (!fundingTxid && vtxos.length > 1) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
"multiple spendable deposits at the swap address \u2014 pass fundingTxid to select one"
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
const vtxo = fundingTxid ? vtxos.find((v) => v.txid === fundingTxid) : vtxos[0];
|
|
884
|
+
if (!vtxo) throw new Error("no spendable VTXO at the swap address");
|
|
885
|
+
if (offer.offerAsset) {
|
|
886
|
+
const offered = offer.offerAsset.toString();
|
|
887
|
+
const deposited = amountOfAsset(vtxo.assets, offered);
|
|
888
|
+
if (deposited <= BigInt(0)) {
|
|
889
|
+
throw new Error(
|
|
890
|
+
`the deposit at the swap address carries no ${offered}, which this offer sells \u2014 filling it would pay wantAmount for nothing. The indexer may be behind, or the offer may not be backed by what it advertises`
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
const payout = payoutScript ?? import_sdk2.ArkAddress.decode(takerAddress).pkScript;
|
|
895
|
+
const makerSats = wantedAssetId === void 0 ? offer.wantAmount : assetCarrierSats;
|
|
896
|
+
const fill = contract.functions.fulfill().from({ txid: vtxo.txid, vout: vtxo.vout, value: vtxo.value }).fund(fund).to(offer.makerPkScript, makerSats).change(payout);
|
|
897
|
+
const held = /* @__PURE__ */ new Map();
|
|
898
|
+
const hold = (vin, assets) => {
|
|
899
|
+
for (const a of assets ?? []) {
|
|
900
|
+
const amount = BigInt(a.amount);
|
|
901
|
+
if (amount <= BigInt(0)) continue;
|
|
902
|
+
held.set(a.assetId, [...held.get(a.assetId) ?? [], { vin, amount }]);
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
hold(0, vtxo.assets);
|
|
906
|
+
fund.forEach((coin, i) => hold(i + 1, coin.assets));
|
|
907
|
+
const outputsSum = makerSats;
|
|
908
|
+
const inputsSum = fund.reduce((s, c) => s + BigInt(c.value), BigInt(vtxo.value));
|
|
909
|
+
const hasPayoutOutput = inputsSum > outputsSum;
|
|
910
|
+
const emitWanted = () => {
|
|
911
|
+
if (wantedAssetId === void 0) return;
|
|
912
|
+
const supplying = held.get(wantedAssetId) ?? [];
|
|
913
|
+
const supplied = supplying.reduce((s, i) => s + i.amount, BigInt(0));
|
|
914
|
+
const outputs = [{ vout: 0, amount: offer.wantAmount }];
|
|
915
|
+
const surplus = supplied - offer.wantAmount;
|
|
916
|
+
if (surplus > BigInt(0)) {
|
|
917
|
+
if (!hasPayoutOutput) {
|
|
918
|
+
throw new Error(
|
|
919
|
+
`fillOffer has ${surplus} of ${wantedAssetId} to return but no payout output \u2014 fund with more sats than the maker's output takes`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
outputs.push({ vout: 1, amount: surplus });
|
|
923
|
+
}
|
|
924
|
+
fill.withAsset({ assetId: wantedAssetId, inputs: supplying, outputs });
|
|
925
|
+
held.delete(wantedAssetId);
|
|
926
|
+
};
|
|
927
|
+
emitWanted();
|
|
928
|
+
for (const [assetId, inputs] of held) {
|
|
929
|
+
const amount = inputs.reduce((s, i) => s + i.amount, BigInt(0));
|
|
930
|
+
if (!hasPayoutOutput) {
|
|
931
|
+
throw new Error(
|
|
932
|
+
`fillOffer has ${amount} of ${assetId} to return but no payout output \u2014 fund with more sats than the maker's output takes`
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
fill.withAsset({ assetId, inputs, outputs: [{ vout: 1, amount }] });
|
|
936
|
+
}
|
|
937
|
+
const { txid } = await fill.send();
|
|
938
|
+
return txid;
|
|
939
|
+
}
|
|
940
|
+
var amountOfAsset = (assets, assetId) => {
|
|
941
|
+
let total = BigInt(0);
|
|
942
|
+
for (const a of assets ?? []) {
|
|
943
|
+
if (a.assetId === assetId) total += BigInt(a.amount);
|
|
944
|
+
}
|
|
945
|
+
return total;
|
|
946
|
+
};
|
|
764
947
|
|
|
765
948
|
// src/markets.ts
|
|
766
949
|
var import_solver_discovery = require("@arkade-os/solver-discovery");
|
|
767
950
|
var import_sdk3 = require("@arkade-os/sdk");
|
|
951
|
+
|
|
952
|
+
// src/marketShape.ts
|
|
953
|
+
var MARKET_CORRIDORS = ["arkade", "bolt11", "bitcoin", "eip155"];
|
|
954
|
+
var LEGACY_CORRIDORS = {
|
|
955
|
+
arkade: "arkade",
|
|
956
|
+
lightning: "bolt11",
|
|
957
|
+
onchain: "bitcoin",
|
|
958
|
+
eip155: "eip155"
|
|
959
|
+
};
|
|
960
|
+
var assetOf = (market, side) => side === "base" ? market.base_asset : market.quote_asset;
|
|
961
|
+
var marketAssetId = (market, side) => {
|
|
962
|
+
const asset3 = assetOf(market, side);
|
|
963
|
+
if (typeof asset3?.caip19_id === "string") return asset3.caip19_id;
|
|
964
|
+
return typeof asset3?.id === "string" ? asset3.id : void 0;
|
|
965
|
+
};
|
|
966
|
+
var chainNamespaceOf = (id) => {
|
|
967
|
+
if (id === void 0) return void 0;
|
|
968
|
+
const slash = id.indexOf("/");
|
|
969
|
+
const colon = id.indexOf(":");
|
|
970
|
+
return slash === -1 || colon === -1 || colon > slash ? void 0 : id.slice(0, colon);
|
|
971
|
+
};
|
|
972
|
+
var marketCorridor = (market, side) => {
|
|
973
|
+
const namespace = chainNamespaceOf(marketAssetId(market, side));
|
|
974
|
+
if (namespace !== void 0) {
|
|
975
|
+
return MARKET_CORRIDORS.includes(namespace) ? namespace : "arkade";
|
|
976
|
+
}
|
|
977
|
+
const legacy = side === "base" ? market.base_corridor : market.quote_corridor;
|
|
978
|
+
return typeof legacy === "string" ? LEGACY_CORRIDORS[legacy] ?? "arkade" : "arkade";
|
|
979
|
+
};
|
|
980
|
+
var isRfqMarket = (market) => marketCorridor(market, "base") !== "arkade" || marketCorridor(market, "quote") !== "arkade";
|
|
981
|
+
var sideLabel = (market, side) => {
|
|
982
|
+
const ticker = assetOf(market, side)?.ticker;
|
|
983
|
+
const label = typeof ticker === "string" && ticker.length > 0 ? ticker : "?";
|
|
984
|
+
const corridor = marketCorridor(market, side);
|
|
985
|
+
return corridor === "arkade" ? label : `${corridor}:${label}`;
|
|
986
|
+
};
|
|
987
|
+
var marketPairLabel = (market) => `${sideLabel(market, "base")}/${sideLabel(market, "quote")}`;
|
|
988
|
+
|
|
989
|
+
// src/markets.ts
|
|
768
990
|
var QUOTE_OPTIONS = { safetyBps: 0 };
|
|
769
991
|
var makeCachedFeedFetch = (ttlMs = 3e4, fetchImpl = fetch) => {
|
|
770
992
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -813,14 +1035,17 @@ var makeCachedFeedFetch = (ttlMs = 3e4, fetchImpl = fetch) => {
|
|
|
813
1035
|
var MARKETS_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
814
1036
|
var isMarketShaped = (m) => {
|
|
815
1037
|
const market = m;
|
|
816
|
-
|
|
1038
|
+
if (!market) return false;
|
|
1039
|
+
return typeof market.base_asset?.id === "string" && typeof market.quote_asset?.id === "string" && typeof market.quote_asset.decimals === "number";
|
|
817
1040
|
};
|
|
818
1041
|
var readMarketsCache = async (repository, network, registry) => {
|
|
819
1042
|
try {
|
|
820
1043
|
const entry = await repository.getCachedMarkets(network, registry);
|
|
821
1044
|
if (!Array.isArray(entry?.markets) || typeof entry?.fetchedAt !== "number")
|
|
822
1045
|
return void 0;
|
|
823
|
-
|
|
1046
|
+
const markets = entry.markets.filter(isMarketShaped);
|
|
1047
|
+
if (entry.markets.length > 0 && markets.length === 0) return void 0;
|
|
1048
|
+
return { ...entry, markets };
|
|
824
1049
|
} catch {
|
|
825
1050
|
return void 0;
|
|
826
1051
|
}
|
|
@@ -861,10 +1086,33 @@ var discoverMarkets = async (options) => {
|
|
|
861
1086
|
};
|
|
862
1087
|
var findMarket = (markets, fromId, toId) => {
|
|
863
1088
|
if (fromId === toId) return void 0;
|
|
864
|
-
const
|
|
1089
|
+
const resolveMarketId = (id) => {
|
|
1090
|
+
for (const market of markets) {
|
|
1091
|
+
for (const side of ["base", "quote"]) {
|
|
1092
|
+
const asset3 = side === "base" ? market.base_asset : market.quote_asset;
|
|
1093
|
+
if (asset3.id === id) return id;
|
|
1094
|
+
const canonical = marketAssetId(market, side) ?? "";
|
|
1095
|
+
const matchesBtc = id === BTC_ASSET_ID && /^arkade:[^/]+\/slip44:(?:0|1)$/.test(canonical);
|
|
1096
|
+
const matchesAsset = /^[0-9a-f]{68}$/.test(id) && canonical.endsWith(`/asset:${id}`);
|
|
1097
|
+
if ((matchesBtc || matchesAsset) && typeof asset3.id === "string") return asset3.id;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return id;
|
|
1101
|
+
};
|
|
1102
|
+
const resolvedFrom = resolveMarketId(fromId);
|
|
1103
|
+
const resolvedTo = resolveMarketId(toId);
|
|
1104
|
+
const givingBase = (0, import_solver_discovery.bestMarket)(markets, {
|
|
1105
|
+
baseId: resolvedFrom,
|
|
1106
|
+
quoteId: resolvedTo,
|
|
1107
|
+
wantSide: "quote"
|
|
1108
|
+
});
|
|
865
1109
|
if (givingBase) return { market: givingBase, give: "base" };
|
|
866
1110
|
return {
|
|
867
|
-
market: (0, import_solver_discovery.bestMarket)(markets, {
|
|
1111
|
+
market: (0, import_solver_discovery.bestMarket)(markets, {
|
|
1112
|
+
baseId: resolvedTo,
|
|
1113
|
+
quoteId: resolvedFrom,
|
|
1114
|
+
wantSide: "base"
|
|
1115
|
+
}),
|
|
868
1116
|
give: "quote"
|
|
869
1117
|
};
|
|
870
1118
|
};
|
|
@@ -1035,8 +1283,316 @@ var IndexedDbAssetSwapRepository = class {
|
|
|
1035
1283
|
}
|
|
1036
1284
|
};
|
|
1037
1285
|
|
|
1286
|
+
// src/restore.ts
|
|
1287
|
+
var import_base3 = require("@scure/base");
|
|
1288
|
+
var import_sdk5 = require("@arkade-os/sdk");
|
|
1289
|
+
var TXS_PER_REQUEST = 50;
|
|
1290
|
+
async function fetchParsedTxs(indexer, txids) {
|
|
1291
|
+
const parsedByTxid = /* @__PURE__ */ new Map();
|
|
1292
|
+
if (txids.length === 0) return parsedByTxid;
|
|
1293
|
+
const chunks = [];
|
|
1294
|
+
for (let i = 0; i < txids.length; i += TXS_PER_REQUEST) {
|
|
1295
|
+
chunks.push(txids.slice(i, i + TXS_PER_REQUEST));
|
|
1296
|
+
}
|
|
1297
|
+
const chunkResults = await Promise.allSettled(
|
|
1298
|
+
chunks.map(async (ids) => (await indexer.getVirtualTxs(ids)).txs)
|
|
1299
|
+
);
|
|
1300
|
+
for (const result of chunkResults) {
|
|
1301
|
+
if (result.status !== "fulfilled") continue;
|
|
1302
|
+
for (const psbt of result.value) {
|
|
1303
|
+
try {
|
|
1304
|
+
const parsed = import_sdk5.Transaction.fromPSBT(import_base3.base64.decode(psbt));
|
|
1305
|
+
parsedByTxid.set(parsed.id, parsed);
|
|
1306
|
+
} catch {
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return parsedByTxid;
|
|
1311
|
+
}
|
|
1312
|
+
var unscannedSwapCandidates = (txs, existingIds, scanned) => txs.filter(
|
|
1313
|
+
(tx) => tx.type === "sent" && tx.redeemTxid && !existingIds.has(tx.redeemTxid) && !scanned.has(tx.redeemTxid)
|
|
1314
|
+
);
|
|
1315
|
+
function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
1316
|
+
let leaves;
|
|
1317
|
+
try {
|
|
1318
|
+
const script = offerVtxoScript(offer, serverPubkey);
|
|
1319
|
+
if (import_base3.hex.encode(script.pkScript) !== import_base3.hex.encode(offer.swapPkScript)) return "indeterminate";
|
|
1320
|
+
leaves = {
|
|
1321
|
+
// both routes that hand the deposit back; `exit` is absent on an
|
|
1322
|
+
// offer that carries no exit closure, and drops out here
|
|
1323
|
+
returned: ["cancel", "exit"].map((name) => script.functionByName(name)?.leafScript).filter((leaf) => leaf !== void 0),
|
|
1324
|
+
fulfill: script.functionByName("fulfill")?.leafScript
|
|
1325
|
+
};
|
|
1326
|
+
} catch {
|
|
1327
|
+
return "indeterminate";
|
|
1328
|
+
}
|
|
1329
|
+
for (let i = 0; i < spendTx.inputsLength; i++) {
|
|
1330
|
+
const input = spendTx.getInput(i);
|
|
1331
|
+
if (!input.txid || input.index !== deposit.vout) continue;
|
|
1332
|
+
if (import_base3.hex.encode(input.txid) !== deposit.txid) continue;
|
|
1333
|
+
for (const leaf of input.tapLeafScript ?? []) {
|
|
1334
|
+
const spent = import_base3.hex.encode((0, import_sdk5.scriptFromTapLeafScript)(leaf));
|
|
1335
|
+
if (leaves.returned.some((back) => spent === import_base3.hex.encode(back))) return "cancelled";
|
|
1336
|
+
if (leaves.fulfill && spent === import_base3.hex.encode(leaves.fulfill)) return "fulfilled";
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return "indeterminate";
|
|
1340
|
+
}
|
|
1341
|
+
var spendTxidsOf = (vtxo) => [vtxo.spentBy, vtxo.arkTxId].filter((id) => Boolean(id));
|
|
1342
|
+
function classifyDepositSpend(offer, serverPubkey, spendTxs, deposit) {
|
|
1343
|
+
for (const tx of spendTxs) {
|
|
1344
|
+
const kind = classifySpend(offer, serverPubkey, tx, deposit);
|
|
1345
|
+
if (kind !== "indeterminate") return kind;
|
|
1346
|
+
}
|
|
1347
|
+
return "indeterminate";
|
|
1348
|
+
}
|
|
1349
|
+
async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
1350
|
+
const { serverPubkey, scanned = /* @__PURE__ */ new Set(), reopen = [] } = opts;
|
|
1351
|
+
const reopened = [];
|
|
1352
|
+
for (const swap of reopen) {
|
|
1353
|
+
try {
|
|
1354
|
+
reopened.push({
|
|
1355
|
+
fundingTx: { type: "sent", redeemTxid: swap.fundingTxid },
|
|
1356
|
+
offer: decodeOffer(import_base3.hex.decode(swap.offerHex)),
|
|
1357
|
+
offerHex: swap.offerHex,
|
|
1358
|
+
existing: swap
|
|
1359
|
+
});
|
|
1360
|
+
} catch {
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
const reopenedTxids = new Set(reopened.map((f) => f.fundingTx.redeemTxid));
|
|
1364
|
+
const candidates = unscannedSwapCandidates(txs, existingIds, scanned).filter(
|
|
1365
|
+
(tx) => !reopenedTxids.has(tx.redeemTxid)
|
|
1366
|
+
);
|
|
1367
|
+
if (candidates.length === 0 && reopened.length === 0) {
|
|
1368
|
+
return { restored: [], scannedTxids: [] };
|
|
1369
|
+
}
|
|
1370
|
+
const byTxid = new Map(candidates.map((tx) => [tx.redeemTxid, tx]));
|
|
1371
|
+
const parsedByTxid = await fetchParsedTxs(
|
|
1372
|
+
indexer,
|
|
1373
|
+
candidates.map((tx) => tx.redeemTxid)
|
|
1374
|
+
);
|
|
1375
|
+
const fetchedTxids = [];
|
|
1376
|
+
const found = [...reopened];
|
|
1377
|
+
for (const [txid, parsed] of parsedByTxid) {
|
|
1378
|
+
const fundingTx = byTxid.get(txid);
|
|
1379
|
+
if (!fundingTx) continue;
|
|
1380
|
+
fetchedTxids.push(txid);
|
|
1381
|
+
try {
|
|
1382
|
+
const packet = import_sdk5.Extension.fromTx(parsed).getPacketByType(OFFER_PACKET_TYPE);
|
|
1383
|
+
if (!packet) continue;
|
|
1384
|
+
const payload = packet.serialize();
|
|
1385
|
+
found.push({
|
|
1386
|
+
fundingTx,
|
|
1387
|
+
offer: decodeOffer(payload),
|
|
1388
|
+
offerHex: import_base3.hex.encode(payload)
|
|
1389
|
+
});
|
|
1390
|
+
} catch {
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (found.length === 0) return { restored: [], scannedTxids: fetchedTxids };
|
|
1394
|
+
const scripts = [...new Set(found.map((f) => import_base3.hex.encode(f.offer.swapPkScript)))];
|
|
1395
|
+
const { vtxos } = await indexer.getVtxos({ scripts });
|
|
1396
|
+
const vtxoByScriptAndTxid = new Map(vtxos.map((v) => [`${v.script}:${v.txid}`, v]));
|
|
1397
|
+
const txByAnyId = /* @__PURE__ */ new Map();
|
|
1398
|
+
for (const tx of txs) {
|
|
1399
|
+
for (const id of [tx.boardingTxid, tx.redeemTxid, tx.roundTxid]) {
|
|
1400
|
+
if (id) txByAnyId.set(id, tx);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const spendTxids = /* @__PURE__ */ new Set();
|
|
1404
|
+
for (const { fundingTx, offer } of found) {
|
|
1405
|
+
const vtxo = vtxoByScriptAndTxid.get(
|
|
1406
|
+
`${import_base3.hex.encode(offer.swapPkScript)}:${fundingTx.redeemTxid}`
|
|
1407
|
+
);
|
|
1408
|
+
if (vtxo?.virtualStatus.state !== "spent") continue;
|
|
1409
|
+
for (const txid of spendTxidsOf(vtxo)) spendTxids.add(txid);
|
|
1410
|
+
}
|
|
1411
|
+
const spendTxByTxid = await fetchParsedTxs(indexer, [...spendTxids]);
|
|
1412
|
+
const restored = [];
|
|
1413
|
+
const unresolved = /* @__PURE__ */ new Set();
|
|
1414
|
+
for (const { fundingTx, offer, offerHex, existing } of found) {
|
|
1415
|
+
const swapPkScript = import_base3.hex.encode(offer.swapPkScript);
|
|
1416
|
+
const vtxo = vtxoByScriptAndTxid.get(`${swapPkScript}:${fundingTx.redeemTxid}`);
|
|
1417
|
+
if (!vtxo) {
|
|
1418
|
+
unresolved.add(fundingTx.redeemTxid);
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
const depositRider = vtxo.assets?.length === 1 && vtxo.assets[0].amount > BigInt(0) ? vtxo.assets[0] : void 0;
|
|
1422
|
+
const fromAsset = offer.offerAsset?.toString() ?? depositRider?.assetId ?? BTC_ASSET_ID;
|
|
1423
|
+
const toAsset = offer.wantAsset?.toString() ?? BTC_ASSET_ID;
|
|
1424
|
+
const depositAmount = fromAsset === BTC_ASSET_ID ? BigInt(vtxo.value) : vtxo.assets?.find((a) => a.assetId === fromAsset)?.amount;
|
|
1425
|
+
if (depositAmount === void 0) {
|
|
1426
|
+
unresolved.add(fundingTx.redeemTxid);
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
const fromAmount = depositAmount.toString();
|
|
1430
|
+
const state = vtxo.virtualStatus.state;
|
|
1431
|
+
const spentTxid = state === "spent" ? vtxo.arkTxId || vtxo.spentBy : void 0;
|
|
1432
|
+
let status = "pending";
|
|
1433
|
+
if (state === "swept") status = "recoverable";
|
|
1434
|
+
else if (state === "spent") {
|
|
1435
|
+
const spendTxs = spendTxidsOf(vtxo).map((id) => spendTxByTxid.get(id)).filter((tx) => tx !== void 0);
|
|
1436
|
+
const kind = classifyDepositSpend(offer, serverPubkey, spendTxs, {
|
|
1437
|
+
txid: vtxo.txid,
|
|
1438
|
+
vout: vtxo.vout
|
|
1439
|
+
});
|
|
1440
|
+
if (kind === "indeterminate") {
|
|
1441
|
+
unresolved.add(fundingTx.redeemTxid);
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
status = kind;
|
|
1445
|
+
}
|
|
1446
|
+
const completion = status === "fulfilled" && spentTxid && txByAnyId.get(spentTxid)?.createdAt ? { completedAt: txByAnyId.get(spentTxid).createdAt * 1e3 } : {};
|
|
1447
|
+
if (existing) {
|
|
1448
|
+
if (status === "pending") continue;
|
|
1449
|
+
restored.push({ ...existing, status, spentTxid, ...completion });
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
restored.push({
|
|
1453
|
+
id: fundingTx.redeemTxid,
|
|
1454
|
+
fromAsset,
|
|
1455
|
+
toAsset,
|
|
1456
|
+
fromAmount,
|
|
1457
|
+
toAmount: offer.wantAmount.toString(),
|
|
1458
|
+
// ponytail(arkade-os/ts-sdk#680): empty address makes cancel fall back
|
|
1459
|
+
// to the current server key; store the funded address if server-key
|
|
1460
|
+
// rotations become real (cancelOffer now at least diagnoses the
|
|
1461
|
+
// mismatch instead of reporting a missing VTXO)
|
|
1462
|
+
swapAddress: "",
|
|
1463
|
+
swapPkScript,
|
|
1464
|
+
offerHex,
|
|
1465
|
+
fundingTxid: fundingTx.redeemTxid,
|
|
1466
|
+
spentTxid,
|
|
1467
|
+
status,
|
|
1468
|
+
createdAt: fundingTx.createdAt ? fundingTx.createdAt * 1e3 : vtxo.createdAt.getTime(),
|
|
1469
|
+
...completion
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
return { restored, scannedTxids: fetchedTxids.filter((id) => !unresolved.has(id)) };
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/restoreRepository.ts
|
|
1476
|
+
var isOpen = (swap) => swap.status === "pending" || swap.status === "cancelling";
|
|
1477
|
+
var isOfferSwap = (swap) => typeof swap.offerHex === "string";
|
|
1478
|
+
var restoreCoverage = async (wallet, arkServerUrl, swaps) => {
|
|
1479
|
+
const offers = swaps.filter(isOfferSwap);
|
|
1480
|
+
if (offers.length > 0) await restoreOfferCoverage(wallet, arkServerUrl, offers);
|
|
1481
|
+
};
|
|
1482
|
+
var aborted = (swaps) => ({
|
|
1483
|
+
swaps,
|
|
1484
|
+
changes: [],
|
|
1485
|
+
scannedTxids: [],
|
|
1486
|
+
aborted: true
|
|
1487
|
+
});
|
|
1488
|
+
async function restoreAssetSwapRepository(opts) {
|
|
1489
|
+
const { wallet, arkServerUrl, indexer, repository, txs, serverPubkey, prepareNew, signal } = opts;
|
|
1490
|
+
const [existing, scanned] = await Promise.all([
|
|
1491
|
+
getAssetSwapsOrThrow(repository),
|
|
1492
|
+
repository.getScannedTxids()
|
|
1493
|
+
]);
|
|
1494
|
+
if (signal?.aborted) return aborted(existing);
|
|
1495
|
+
let scan;
|
|
1496
|
+
try {
|
|
1497
|
+
scan = await restoreAssetSwaps(indexer, txs, new Set(existing.map((swap) => swap.id)), {
|
|
1498
|
+
serverPubkey,
|
|
1499
|
+
scanned,
|
|
1500
|
+
reopen: existing.filter((swap) => isOpen(swap) && isOfferSwap(swap))
|
|
1501
|
+
});
|
|
1502
|
+
} catch (scanError) {
|
|
1503
|
+
if (!signal?.aborted) {
|
|
1504
|
+
try {
|
|
1505
|
+
await restoreCoverage(wallet, arkServerUrl, existing);
|
|
1506
|
+
} catch (coverageError2) {
|
|
1507
|
+
throw new AggregateError(
|
|
1508
|
+
[scanError, coverageError2],
|
|
1509
|
+
"asset-swap scan and covenant coverage restore both failed"
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
throw scanError;
|
|
1514
|
+
}
|
|
1515
|
+
if (signal?.aborted) return aborted(existing);
|
|
1516
|
+
const before = new Map(existing.map((swap) => [swap.id, swap]));
|
|
1517
|
+
const changes = [];
|
|
1518
|
+
const cancelledAfterCommit = async (scannedTxids = []) => ({
|
|
1519
|
+
swaps: await getAssetSwapsOrThrow(repository),
|
|
1520
|
+
changes,
|
|
1521
|
+
scannedTxids,
|
|
1522
|
+
aborted: true
|
|
1523
|
+
});
|
|
1524
|
+
for (const restored of scan.restored) {
|
|
1525
|
+
if (signal?.aborted) return cancelledAfterCommit();
|
|
1526
|
+
const previous = before.get(restored.id);
|
|
1527
|
+
const current = previous !== void 0 || !prepareNew ? restored : await prepareNew(restored);
|
|
1528
|
+
if (current.id !== restored.id) {
|
|
1529
|
+
throw new Error("prepareNew must not change an asset swap id");
|
|
1530
|
+
}
|
|
1531
|
+
if (signal?.aborted) return cancelledAfterCommit();
|
|
1532
|
+
await repository.saveSwap(current);
|
|
1533
|
+
changes.push(previous ? { previous, current } : { current });
|
|
1534
|
+
if (signal?.aborted) return cancelledAfterCommit();
|
|
1535
|
+
}
|
|
1536
|
+
if (signal?.aborted) return cancelledAfterCommit();
|
|
1537
|
+
if (scan.scannedTxids.length > 0) {
|
|
1538
|
+
await repository.markTxidsScanned(scan.scannedTxids);
|
|
1539
|
+
}
|
|
1540
|
+
const swaps = await getAssetSwapsOrThrow(repository);
|
|
1541
|
+
if (signal?.aborted) return cancelledAfterCommit(scan.scannedTxids);
|
|
1542
|
+
let coverageError;
|
|
1543
|
+
try {
|
|
1544
|
+
await restoreCoverage(wallet, arkServerUrl, swaps);
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
coverageError = error;
|
|
1547
|
+
}
|
|
1548
|
+
return {
|
|
1549
|
+
swaps,
|
|
1550
|
+
changes,
|
|
1551
|
+
scannedTxids: scan.scannedTxids,
|
|
1552
|
+
aborted: false,
|
|
1553
|
+
...coverageError === void 0 ? {} : { coverageError }
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// src/registerRestore.ts
|
|
1558
|
+
var import_sdk6 = require("@arkade-os/sdk");
|
|
1559
|
+
var toRestoreTx = (tx) => ({
|
|
1560
|
+
type: tx.type.toLowerCase(),
|
|
1561
|
+
redeemTxid: tx.key.arkTxid,
|
|
1562
|
+
boardingTxid: tx.key.boardingTxid,
|
|
1563
|
+
roundTxid: tx.key.commitmentTxid,
|
|
1564
|
+
createdAt: Math.floor(tx.createdAt / 1e3)
|
|
1565
|
+
});
|
|
1566
|
+
function registerAssetSwapRestore(wallet, options) {
|
|
1567
|
+
return (0, import_sdk6.registerWalletRestoreHook)(wallet, {
|
|
1568
|
+
id: "arkade-os:asset-swap",
|
|
1569
|
+
restore: async (restoredWallet) => {
|
|
1570
|
+
const dependencies = restoredWallet;
|
|
1571
|
+
const indexer = options.indexer ?? dependencies.indexerProvider;
|
|
1572
|
+
const serverPubkey = options.serverPubkey ?? dependencies.arkServerPublicKey;
|
|
1573
|
+
if (!indexer) {
|
|
1574
|
+
throw new Error("asset-swap restore requires an indexer");
|
|
1575
|
+
}
|
|
1576
|
+
if (!serverPubkey) {
|
|
1577
|
+
throw new Error("asset-swap restore requires an Ark server public key");
|
|
1578
|
+
}
|
|
1579
|
+
const history = await restoredWallet.getTransactionHistory();
|
|
1580
|
+
const result = await restoreAssetSwapRepository({
|
|
1581
|
+
wallet: restoredWallet,
|
|
1582
|
+
arkServerUrl: options.arkServerUrl,
|
|
1583
|
+
indexer,
|
|
1584
|
+
repository: options.repository,
|
|
1585
|
+
txs: history.map(toRestoreTx),
|
|
1586
|
+
serverPubkey,
|
|
1587
|
+
prepareNew: options.prepareNew
|
|
1588
|
+
});
|
|
1589
|
+
await options.onResult?.(result);
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1038
1594
|
// src/rfqCorridors.ts
|
|
1039
|
-
var
|
|
1595
|
+
var import_base5 = require("@scure/base");
|
|
1040
1596
|
|
|
1041
1597
|
// src/rfqCorridor.ts
|
|
1042
1598
|
var RfqCorridorRegistry = class {
|
|
@@ -1155,7 +1711,7 @@ var rfqClaimSecretOf = (record) => {
|
|
|
1155
1711
|
};
|
|
1156
1712
|
|
|
1157
1713
|
// src/onchainHtlc.ts
|
|
1158
|
-
var
|
|
1714
|
+
var import_base4 = require("@scure/base");
|
|
1159
1715
|
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
1160
1716
|
var import_legacy = require("@noble/hashes/legacy.js");
|
|
1161
1717
|
var btc = __toESM(require("@scure/btc-signer"), 1);
|
|
@@ -1166,8 +1722,8 @@ var LOCKTIME_THRESHOLD = 5e8;
|
|
|
1166
1722
|
var ONCHAIN_SECONDS_PER_BLOCK = 600;
|
|
1167
1723
|
var ONCHAIN_DUST_SATS = BigInt(330);
|
|
1168
1724
|
var newPreimage = () => crypto.getRandomValues(new Uint8Array(32));
|
|
1169
|
-
var paymentHashOf = (preimage) =>
|
|
1170
|
-
var h160FromPaymentHash = (paymentHash) => (0, import_legacy.ripemd160)(
|
|
1725
|
+
var paymentHashOf = (preimage) => import_base4.hex.encode((0, import_sha22.sha256)(preimage));
|
|
1726
|
+
var h160FromPaymentHash = (paymentHash) => (0, import_legacy.ripemd160)(import_base4.hex.decode(paymentHash));
|
|
1171
1727
|
var L1_NETWORKS = {
|
|
1172
1728
|
bitcoin: btc.NETWORK,
|
|
1173
1729
|
testnet: btc.TEST_NETWORK,
|
|
@@ -1219,7 +1775,7 @@ function onchainHtlcScript(params, network) {
|
|
|
1219
1775
|
);
|
|
1220
1776
|
const controlBlockFor = (leaf) => {
|
|
1221
1777
|
for (const [block, script] of payment.tapLeafScript ?? []) {
|
|
1222
|
-
if (script.length - 1 === leaf.length &&
|
|
1778
|
+
if (script.length - 1 === leaf.length && import_base4.hex.encode(script.subarray(0, leaf.length)) === import_base4.hex.encode(leaf)) {
|
|
1223
1779
|
return btc.TaprootControlBlock.encode(block);
|
|
1224
1780
|
}
|
|
1225
1781
|
}
|
|
@@ -1312,13 +1868,13 @@ var buildHtlcRefund = (input) => buildLeafSpend({
|
|
|
1312
1868
|
function extractPreimage(txHex, paymentHash) {
|
|
1313
1869
|
let raw;
|
|
1314
1870
|
try {
|
|
1315
|
-
raw = btc.RawTx.decode(
|
|
1871
|
+
raw = btc.RawTx.decode(import_base4.hex.decode(txHex));
|
|
1316
1872
|
} catch {
|
|
1317
1873
|
return null;
|
|
1318
1874
|
}
|
|
1319
1875
|
for (const stack of raw.witnesses ?? []) {
|
|
1320
1876
|
for (const item of stack) {
|
|
1321
|
-
if (item.length === 32 &&
|
|
1877
|
+
if (item.length === 32 && import_base4.hex.encode((0, import_sha22.sha256)(item)) === paymentHash) return item;
|
|
1322
1878
|
}
|
|
1323
1879
|
}
|
|
1324
1880
|
return null;
|
|
@@ -1363,7 +1919,7 @@ async function classifyOnchainHtlc(chain, input) {
|
|
|
1363
1919
|
);
|
|
1364
1920
|
if (!spend) return { phase: "unfunded" };
|
|
1365
1921
|
const preimage = extractPreimage(spend.txHex, input.htlc.paymentHash);
|
|
1366
|
-
const txid = btc.Transaction.fromRaw(
|
|
1922
|
+
const txid = btc.Transaction.fromRaw(import_base4.hex.decode(spend.txHex), {
|
|
1367
1923
|
allowUnknownInputs: true,
|
|
1368
1924
|
allowUnknownOutputs: true
|
|
1369
1925
|
}).id;
|
|
@@ -1414,13 +1970,13 @@ var LightningReceiveCorridor = {
|
|
|
1414
1970
|
};
|
|
1415
1971
|
function onchainSendProfile(result) {
|
|
1416
1972
|
return {
|
|
1417
|
-
claimKey:
|
|
1418
|
-
refundKey:
|
|
1973
|
+
claimKey: import_base5.hex.encode(result.htlcParams.claimKey),
|
|
1974
|
+
refundKey: import_base5.hex.encode(result.htlcParams.refundKey),
|
|
1419
1975
|
htlcLocktime: result.htlcParams.refundLocktime,
|
|
1420
1976
|
network: result.l1Network,
|
|
1421
1977
|
htlcAddress: result.htlc.address,
|
|
1422
1978
|
minConfirmations: result.minConfirmations,
|
|
1423
|
-
payoutPkScript:
|
|
1979
|
+
payoutPkScript: import_base5.hex.encode(result.payoutPkScript)
|
|
1424
1980
|
};
|
|
1425
1981
|
}
|
|
1426
1982
|
var OnchainSendCorridor = {
|
|
@@ -1454,8 +2010,8 @@ var OnchainSendCorridor = {
|
|
|
1454
2010
|
// unlocks both legs, but only one of the two hashes of it
|
|
1455
2011
|
// is recoverable here.
|
|
1456
2012
|
paymentHash,
|
|
1457
|
-
claimKey:
|
|
1458
|
-
refundKey:
|
|
2013
|
+
claimKey: import_base5.hex.decode(profile.claimKey),
|
|
2014
|
+
refundKey: import_base5.hex.decode(profile.refundKey),
|
|
1459
2015
|
refundLocktime: profile.htlcLocktime
|
|
1460
2016
|
},
|
|
1461
2017
|
profile.network
|
|
@@ -1471,7 +2027,7 @@ var OnchainSendCorridor = {
|
|
|
1471
2027
|
minConfirmations: profile.minConfirmations,
|
|
1472
2028
|
// Optional here, required at the write: throwing on an older
|
|
1473
2029
|
// record would strand the refund it is still owed.
|
|
1474
|
-
...profile.payoutPkScript ? { payoutPkScript:
|
|
2030
|
+
...profile.payoutPkScript ? { payoutPkScript: import_base5.hex.decode(profile.payoutPkScript) } : {},
|
|
1475
2031
|
...profile.funding ? { funding: profile.funding } : {},
|
|
1476
2032
|
...profile.claimTxid ? { claimTxid: profile.claimTxid } : {}
|
|
1477
2033
|
};
|
|
@@ -1489,8 +2045,8 @@ rfqCorridorHandlers.register(LightningReceiveCorridor);
|
|
|
1489
2045
|
rfqCorridorHandlers.register(OnchainSendCorridor);
|
|
1490
2046
|
|
|
1491
2047
|
// src/rfqRecord.ts
|
|
1492
|
-
var
|
|
1493
|
-
var
|
|
2048
|
+
var import_sdk7 = require("@arkade-os/sdk");
|
|
2049
|
+
var import_base6 = require("@scure/base");
|
|
1494
2050
|
|
|
1495
2051
|
// src/rfqSwapState.ts
|
|
1496
2052
|
var RFQ_SWAP_TERMINAL_STATES = ["settled", "refunded", "failed"];
|
|
@@ -1514,8 +2070,8 @@ function assertSameSwap(origin, swap) {
|
|
|
1514
2070
|
`rfq swap record is a ${origin.kind} origin paired with a ${swap.kind} swap`
|
|
1515
2071
|
);
|
|
1516
2072
|
}
|
|
1517
|
-
const funded =
|
|
1518
|
-
const watched =
|
|
2073
|
+
const funded = import_base6.hex.encode(import_sdk7.ArkAddress.decode(origin.lockupAddress).pkScript);
|
|
2074
|
+
const watched = import_base6.hex.encode(swap.lockupPkScript);
|
|
1519
2075
|
if (funded !== watched) {
|
|
1520
2076
|
throw new Error(
|
|
1521
2077
|
`rfq swap record's lockup address holds ${funded}, but the swap watches ${watched} \u2014 these are not the same swap`
|
|
@@ -1559,11 +2115,11 @@ function rfqSwapOriginOf(record) {
|
|
|
1559
2115
|
};
|
|
1560
2116
|
}
|
|
1561
2117
|
function lockupScript(params, lockupAddress) {
|
|
1562
|
-
const script =
|
|
1563
|
-
const funded =
|
|
1564
|
-
if (
|
|
2118
|
+
const script = import_sdk7.VHTLCV2ContractHandler.createScript(params);
|
|
2119
|
+
const funded = import_sdk7.ArkAddress.decode(lockupAddress).pkScript;
|
|
2120
|
+
if (import_base6.hex.encode(funded) !== import_base6.hex.encode(script.pkScript)) {
|
|
1565
2121
|
throw new Error(
|
|
1566
|
-
`rfq swap covenant params derive ${
|
|
2122
|
+
`rfq swap covenant params derive ${import_base6.hex.encode(script.pkScript)}, but the record's lockup address holds ${import_base6.hex.encode(funded)} \u2014 these params are not this swap's`
|
|
1567
2123
|
);
|
|
1568
2124
|
}
|
|
1569
2125
|
return script;
|
|
@@ -1597,177 +2153,9 @@ function shouldRetainRfqSwap(record, now) {
|
|
|
1597
2153
|
return now - record.updatedAt < RFQ_SWAP_RETENTION_SECONDS;
|
|
1598
2154
|
}
|
|
1599
2155
|
|
|
1600
|
-
// src/restore.ts
|
|
1601
|
-
var import_base6 = require("@scure/base");
|
|
1602
|
-
var import_sdk6 = require("@arkade-os/sdk");
|
|
1603
|
-
var TXS_PER_REQUEST = 50;
|
|
1604
|
-
async function fetchParsedTxs(indexer, txids) {
|
|
1605
|
-
const parsedByTxid = /* @__PURE__ */ new Map();
|
|
1606
|
-
if (txids.length === 0) return parsedByTxid;
|
|
1607
|
-
const chunks = [];
|
|
1608
|
-
for (let i = 0; i < txids.length; i += TXS_PER_REQUEST) {
|
|
1609
|
-
chunks.push(txids.slice(i, i + TXS_PER_REQUEST));
|
|
1610
|
-
}
|
|
1611
|
-
const chunkResults = await Promise.allSettled(
|
|
1612
|
-
chunks.map(async (ids) => (await indexer.getVirtualTxs(ids)).txs)
|
|
1613
|
-
);
|
|
1614
|
-
for (const result of chunkResults) {
|
|
1615
|
-
if (result.status !== "fulfilled") continue;
|
|
1616
|
-
for (const psbt of result.value) {
|
|
1617
|
-
try {
|
|
1618
|
-
const parsed = import_sdk6.Transaction.fromPSBT(import_base6.base64.decode(psbt));
|
|
1619
|
-
parsedByTxid.set(parsed.id, parsed);
|
|
1620
|
-
} catch {
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
return parsedByTxid;
|
|
1625
|
-
}
|
|
1626
|
-
var unscannedSwapCandidates = (txs, existingIds, scanned) => txs.filter(
|
|
1627
|
-
(tx) => tx.type === "sent" && tx.redeemTxid && !existingIds.has(tx.redeemTxid) && !scanned.has(tx.redeemTxid)
|
|
1628
|
-
);
|
|
1629
|
-
function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
1630
|
-
let leaves;
|
|
1631
|
-
try {
|
|
1632
|
-
const script = offerVtxoScript(offer, serverPubkey);
|
|
1633
|
-
if (import_base6.hex.encode(script.pkScript) !== import_base6.hex.encode(offer.swapPkScript)) return "indeterminate";
|
|
1634
|
-
leaves = {
|
|
1635
|
-
// both routes that hand the deposit back; `exit` is absent on an
|
|
1636
|
-
// offer that carries no exit closure, and drops out here
|
|
1637
|
-
returned: ["cancel", "exit"].map((name) => script.functionByName(name)?.leafScript).filter((leaf) => leaf !== void 0),
|
|
1638
|
-
fulfill: script.functionByName("fulfill")?.leafScript
|
|
1639
|
-
};
|
|
1640
|
-
} catch {
|
|
1641
|
-
return "indeterminate";
|
|
1642
|
-
}
|
|
1643
|
-
for (let i = 0; i < spendTx.inputsLength; i++) {
|
|
1644
|
-
const input = spendTx.getInput(i);
|
|
1645
|
-
if (!input.txid || input.index !== deposit.vout) continue;
|
|
1646
|
-
if (import_base6.hex.encode(input.txid) !== deposit.txid) continue;
|
|
1647
|
-
for (const leaf of input.tapLeafScript ?? []) {
|
|
1648
|
-
const spent = import_base6.hex.encode((0, import_sdk6.scriptFromTapLeafScript)(leaf));
|
|
1649
|
-
if (leaves.returned.some((back) => spent === import_base6.hex.encode(back))) return "cancelled";
|
|
1650
|
-
if (leaves.fulfill && spent === import_base6.hex.encode(leaves.fulfill)) return "fulfilled";
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
return "indeterminate";
|
|
1654
|
-
}
|
|
1655
|
-
var spendTxidsOf = (vtxo) => [vtxo.spentBy, vtxo.arkTxId].filter((id) => Boolean(id));
|
|
1656
|
-
function classifyDepositSpend(offer, serverPubkey, spendTxs, deposit) {
|
|
1657
|
-
for (const tx of spendTxs) {
|
|
1658
|
-
const kind = classifySpend(offer, serverPubkey, tx, deposit);
|
|
1659
|
-
if (kind !== "indeterminate") return kind;
|
|
1660
|
-
}
|
|
1661
|
-
return "indeterminate";
|
|
1662
|
-
}
|
|
1663
|
-
async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
1664
|
-
const { serverPubkey, scanned = /* @__PURE__ */ new Set() } = opts;
|
|
1665
|
-
const candidates = unscannedSwapCandidates(txs, existingIds, scanned);
|
|
1666
|
-
if (candidates.length === 0) return { restored: [], scannedTxids: [] };
|
|
1667
|
-
const byTxid = new Map(candidates.map((tx) => [tx.redeemTxid, tx]));
|
|
1668
|
-
const parsedByTxid = await fetchParsedTxs(
|
|
1669
|
-
indexer,
|
|
1670
|
-
candidates.map((tx) => tx.redeemTxid)
|
|
1671
|
-
);
|
|
1672
|
-
const fetchedTxids = [];
|
|
1673
|
-
const found = [];
|
|
1674
|
-
for (const [txid, parsed] of parsedByTxid) {
|
|
1675
|
-
const fundingTx = byTxid.get(txid);
|
|
1676
|
-
if (!fundingTx) continue;
|
|
1677
|
-
fetchedTxids.push(txid);
|
|
1678
|
-
try {
|
|
1679
|
-
const packet = import_sdk6.Extension.fromTx(parsed).getPacketByType(OFFER_PACKET_TYPE);
|
|
1680
|
-
if (!packet) continue;
|
|
1681
|
-
const payload = packet.serialize();
|
|
1682
|
-
found.push({
|
|
1683
|
-
fundingTx,
|
|
1684
|
-
offer: decodeOffer(payload),
|
|
1685
|
-
offerHex: import_base6.hex.encode(payload)
|
|
1686
|
-
});
|
|
1687
|
-
} catch {
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
if (found.length === 0) return { restored: [], scannedTxids: fetchedTxids };
|
|
1691
|
-
const scripts = [...new Set(found.map((f) => import_base6.hex.encode(f.offer.swapPkScript)))];
|
|
1692
|
-
const { vtxos } = await indexer.getVtxos({ scripts });
|
|
1693
|
-
const vtxoByScriptAndTxid = new Map(vtxos.map((v) => [`${v.script}:${v.txid}`, v]));
|
|
1694
|
-
const txByAnyId = /* @__PURE__ */ new Map();
|
|
1695
|
-
for (const tx of txs) {
|
|
1696
|
-
for (const id of [tx.boardingTxid, tx.redeemTxid, tx.roundTxid]) {
|
|
1697
|
-
if (id) txByAnyId.set(id, tx);
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
const spendTxids = /* @__PURE__ */ new Set();
|
|
1701
|
-
for (const { fundingTx, offer } of found) {
|
|
1702
|
-
const vtxo = vtxoByScriptAndTxid.get(
|
|
1703
|
-
`${import_base6.hex.encode(offer.swapPkScript)}:${fundingTx.redeemTxid}`
|
|
1704
|
-
);
|
|
1705
|
-
if (vtxo?.virtualStatus.state !== "spent") continue;
|
|
1706
|
-
for (const txid of spendTxidsOf(vtxo)) spendTxids.add(txid);
|
|
1707
|
-
}
|
|
1708
|
-
const spendTxByTxid = await fetchParsedTxs(indexer, [...spendTxids]);
|
|
1709
|
-
const restored = [];
|
|
1710
|
-
const unresolved = /* @__PURE__ */ new Set();
|
|
1711
|
-
for (const { fundingTx, offer, offerHex } of found) {
|
|
1712
|
-
const swapPkScript = import_base6.hex.encode(offer.swapPkScript);
|
|
1713
|
-
const vtxo = vtxoByScriptAndTxid.get(`${swapPkScript}:${fundingTx.redeemTxid}`);
|
|
1714
|
-
if (!vtxo) {
|
|
1715
|
-
unresolved.add(fundingTx.redeemTxid);
|
|
1716
|
-
continue;
|
|
1717
|
-
}
|
|
1718
|
-
const depositRider = vtxo.assets?.length === 1 && vtxo.assets[0].amount > BigInt(0) ? vtxo.assets[0] : void 0;
|
|
1719
|
-
const fromAsset = offer.offerAsset?.toString() ?? depositRider?.assetId ?? BTC_ASSET_ID;
|
|
1720
|
-
const toAsset = offer.wantAsset?.toString() ?? BTC_ASSET_ID;
|
|
1721
|
-
const depositAmount = fromAsset === BTC_ASSET_ID ? BigInt(vtxo.value) : vtxo.assets?.find((a) => a.assetId === fromAsset)?.amount;
|
|
1722
|
-
if (depositAmount === void 0) {
|
|
1723
|
-
unresolved.add(fundingTx.redeemTxid);
|
|
1724
|
-
continue;
|
|
1725
|
-
}
|
|
1726
|
-
const fromAmount = depositAmount.toString();
|
|
1727
|
-
const state = vtxo.virtualStatus.state;
|
|
1728
|
-
const spentTxid = state === "spent" ? vtxo.arkTxId || vtxo.spentBy : void 0;
|
|
1729
|
-
let status = "pending";
|
|
1730
|
-
if (state === "swept") status = "recoverable";
|
|
1731
|
-
else if (state === "spent") {
|
|
1732
|
-
const spendTxs = spendTxidsOf(vtxo).map((id) => spendTxByTxid.get(id)).filter((tx) => tx !== void 0);
|
|
1733
|
-
const kind = classifyDepositSpend(offer, serverPubkey, spendTxs, {
|
|
1734
|
-
txid: vtxo.txid,
|
|
1735
|
-
vout: vtxo.vout
|
|
1736
|
-
});
|
|
1737
|
-
if (kind === "indeterminate") {
|
|
1738
|
-
unresolved.add(fundingTx.redeemTxid);
|
|
1739
|
-
continue;
|
|
1740
|
-
}
|
|
1741
|
-
status = kind;
|
|
1742
|
-
}
|
|
1743
|
-
restored.push({
|
|
1744
|
-
id: fundingTx.redeemTxid,
|
|
1745
|
-
fromAsset,
|
|
1746
|
-
toAsset,
|
|
1747
|
-
fromAmount,
|
|
1748
|
-
toAmount: offer.wantAmount.toString(),
|
|
1749
|
-
// ponytail(arkade-os/ts-sdk#680): empty address makes cancel fall back
|
|
1750
|
-
// to the current server key; store the funded address if server-key
|
|
1751
|
-
// rotations become real (cancelOffer now at least diagnoses the
|
|
1752
|
-
// mismatch instead of reporting a missing VTXO)
|
|
1753
|
-
swapAddress: "",
|
|
1754
|
-
swapPkScript,
|
|
1755
|
-
offerHex,
|
|
1756
|
-
fundingTxid: fundingTx.redeemTxid,
|
|
1757
|
-
spentTxid,
|
|
1758
|
-
status,
|
|
1759
|
-
createdAt: fundingTx.createdAt ? fundingTx.createdAt * 1e3 : vtxo.createdAt.getTime(),
|
|
1760
|
-
// the completion time is the caller's record of the spend, if it
|
|
1761
|
-
// has one — the psbt that classified it carries no timestamp
|
|
1762
|
-
...status === "fulfilled" && spentTxid && txByAnyId.get(spentTxid)?.createdAt ? { completedAt: txByAnyId.get(spentTxid).createdAt * 1e3 } : {}
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
return { restored, scannedTxids: fetchedTxids.filter((id) => !unresolved.has(id)) };
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
2156
|
// src/watch.ts
|
|
1769
2157
|
var import_base7 = require("@scure/base");
|
|
1770
|
-
var
|
|
2158
|
+
var import_sdk8 = require("@arkade-os/sdk");
|
|
1771
2159
|
var TERMINAL = ["fulfilled", "cancelled", "recoverable"];
|
|
1772
2160
|
function spendUpdate(swap, spend) {
|
|
1773
2161
|
if (TERMINAL.includes(swap.status)) return void 0;
|
|
@@ -1787,8 +2175,8 @@ async function watchOfferSwaps({
|
|
|
1787
2175
|
onUpdate
|
|
1788
2176
|
}) {
|
|
1789
2177
|
const manager = await wallet.getContractManager();
|
|
1790
|
-
const serverPubkey =
|
|
1791
|
-
const indexer = new
|
|
2178
|
+
const serverPubkey = import_sdk8.ArkAddress.decode(await wallet.getAddress()).serverPubKey;
|
|
2179
|
+
const indexer = new import_sdk8.RestIndexerProvider(arkServerUrl);
|
|
1792
2180
|
let queue = Promise.resolve();
|
|
1793
2181
|
const enqueue = (task) => {
|
|
1794
2182
|
queue = queue.then(task).catch(() => {
|
|
@@ -1803,41 +2191,61 @@ async function watchOfferSwaps({
|
|
|
1803
2191
|
return classifyDepositSpend(
|
|
1804
2192
|
decodeOffer(import_base7.hex.decode(swap.offerHex)),
|
|
1805
2193
|
serverPubkey,
|
|
1806
|
-
txs.map((psbt) =>
|
|
2194
|
+
txs.map((psbt) => import_sdk8.Transaction.fromPSBT(import_base7.base64.decode(psbt))),
|
|
1807
2195
|
{ txid: vtxo.txid, vout: vtxo.vout }
|
|
1808
2196
|
);
|
|
1809
2197
|
} catch {
|
|
1810
2198
|
return "indeterminate";
|
|
1811
2199
|
}
|
|
1812
2200
|
};
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1815
|
-
for (const vtxo of
|
|
2201
|
+
const resolveSpends = async (contractScript, vtxos, { at, known } = {}) => {
|
|
2202
|
+
let swaps = known ?? await getAssetSwaps(repository);
|
|
2203
|
+
for (const vtxo of vtxos) {
|
|
1816
2204
|
const spentTxid = vtxo.arkTxId || vtxo.spentBy;
|
|
1817
2205
|
if (!spentTxid) continue;
|
|
1818
|
-
const swap =
|
|
1819
|
-
(s) => s.fundingTxid === vtxo.txid && s.swapPkScript ===
|
|
2206
|
+
const swap = swaps.find(
|
|
2207
|
+
(s) => s.fundingTxid === vtxo.txid && s.swapPkScript === contractScript
|
|
1820
2208
|
);
|
|
1821
2209
|
if (!swap) continue;
|
|
1822
2210
|
const kind = await classify(swap, vtxo, spentTxid);
|
|
1823
|
-
const changes = spendUpdate(swap, { txid: spentTxid, kind, at
|
|
2211
|
+
const changes = spendUpdate(swap, { txid: spentTxid, kind, at });
|
|
1824
2212
|
if (!changes) continue;
|
|
1825
|
-
const { persisted, swaps } = await updateAssetSwapBestEffort(
|
|
2213
|
+
const { persisted, swaps: written } = await updateAssetSwapBestEffort(
|
|
1826
2214
|
repository,
|
|
1827
2215
|
swap.id,
|
|
1828
2216
|
changes
|
|
1829
2217
|
);
|
|
1830
2218
|
if (!persisted) continue;
|
|
2219
|
+
swaps = written;
|
|
1831
2220
|
onUpdate?.({ ...swap, ...changes });
|
|
1832
2221
|
if (changes.status && RETIRABLE.includes(changes.status)) {
|
|
1833
|
-
await retireOfferContract(manager, swaps,
|
|
2222
|
+
await retireOfferContract(manager, swaps, contractScript);
|
|
1834
2223
|
}
|
|
1835
2224
|
}
|
|
1836
2225
|
};
|
|
2226
|
+
const handleSpend = async (event) => {
|
|
2227
|
+
if (event.contract.metadata?.kind !== OFFER_CONTRACT_KIND) return;
|
|
2228
|
+
await resolveSpends(event.contractScript, event.vtxos, { at: event.timestamp });
|
|
2229
|
+
};
|
|
2230
|
+
const resolveOpenSwaps = async () => {
|
|
2231
|
+
const swaps = await getAssetSwaps(repository);
|
|
2232
|
+
const open = swaps.filter((s) => !TERMINAL.includes(s.status));
|
|
2233
|
+
if (open.length === 0) return;
|
|
2234
|
+
const script = [...new Set(open.map((s) => s.swapPkScript))];
|
|
2235
|
+
for (const { contract, vtxos } of await manager.getContractsWithVtxos({ script })) {
|
|
2236
|
+
if (contract.metadata?.kind !== OFFER_CONTRACT_KIND) continue;
|
|
2237
|
+
await resolveSpends(
|
|
2238
|
+
contract.script,
|
|
2239
|
+
vtxos.filter((v) => v.isSpent),
|
|
2240
|
+
{ known: swaps }
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
1837
2244
|
const unsubscribe = manager.onContractEvent((event) => {
|
|
1838
|
-
if (!(0,
|
|
2245
|
+
if (!(0, import_sdk8.isContractVtxoEvent)(event) || event.type !== "vtxo_spent") return;
|
|
1839
2246
|
enqueue(() => handleSpend(event));
|
|
1840
2247
|
});
|
|
2248
|
+
enqueue(resolveOpenSwaps);
|
|
1841
2249
|
return {
|
|
1842
2250
|
stop: unsubscribe,
|
|
1843
2251
|
idle: () => queue
|
|
@@ -1847,8 +2255,8 @@ async function watchOfferSwaps({
|
|
|
1847
2255
|
// src/rfq.ts
|
|
1848
2256
|
var import_base10 = require("@scure/base");
|
|
1849
2257
|
var import_legacy2 = require("@noble/hashes/legacy.js");
|
|
1850
|
-
var import_sdk9 = require("@arkade-os/sdk");
|
|
1851
2258
|
var import_sdk10 = require("@arkade-os/sdk");
|
|
2259
|
+
var import_sdk11 = require("@arkade-os/sdk");
|
|
1852
2260
|
|
|
1853
2261
|
// src/claimPacket.ts
|
|
1854
2262
|
var import_base8 = require("@scure/base");
|
|
@@ -1883,7 +2291,7 @@ async function sealWithEntropy(input, ephemeralKey, nonce) {
|
|
|
1883
2291
|
|
|
1884
2292
|
// src/lockupContract.ts
|
|
1885
2293
|
var import_base9 = require("@scure/base");
|
|
1886
|
-
var
|
|
2294
|
+
var import_sdk9 = require("@arkade-os/sdk");
|
|
1887
2295
|
var SWAP_LOCKUP_CONTRACT_TYPE = "vhtlc-v2";
|
|
1888
2296
|
var SWAP_LOCKUP_CONTRACT_LABEL = "Arkade RFQ swap lockup";
|
|
1889
2297
|
var SWAP_LOCKUP_CONTRACT_KIND = "rfq-swap-lockup";
|
|
@@ -1919,7 +2327,7 @@ async function registerLockupContract(contracts, script, address) {
|
|
|
1919
2327
|
try {
|
|
1920
2328
|
await contracts.createContract({
|
|
1921
2329
|
type: SWAP_LOCKUP_CONTRACT_TYPE,
|
|
1922
|
-
params:
|
|
2330
|
+
params: import_sdk9.VHTLCV2ContractHandler.serializeParams(script.options),
|
|
1923
2331
|
script: import_base9.hex.encode(script.pkScript),
|
|
1924
2332
|
address,
|
|
1925
2333
|
label: SWAP_LOCKUP_CONTRACT_LABEL,
|
|
@@ -1930,7 +2338,7 @@ async function registerLockupContract(contracts, script, address) {
|
|
|
1930
2338
|
}
|
|
1931
2339
|
}
|
|
1932
2340
|
async function lockupContractParams(contracts, lockupAddress) {
|
|
1933
|
-
const script = import_base9.hex.encode(
|
|
2341
|
+
const script = import_base9.hex.encode(import_sdk9.ArkAddress.decode(lockupAddress).pkScript);
|
|
1934
2342
|
const [row] = await contracts.getContracts({ script });
|
|
1935
2343
|
if (!row) throw new LockupContractMissing(lockupAddress, script);
|
|
1936
2344
|
return row.params;
|
|
@@ -1955,14 +2363,78 @@ var LIGHTNING_RECEIVE_PAIR = rfqPair(LIGHTNING_BTC, ARKADE_BTC);
|
|
|
1955
2363
|
var ONCHAIN_SEND_PAIR = rfqPair(ARKADE_BTC, ONCHAIN_BTC);
|
|
1956
2364
|
var ONCHAIN_RECEIVE_PAIR = rfqPair(ONCHAIN_BTC, ARKADE_BTC);
|
|
1957
2365
|
var RFQ_TERMINAL_STATES = ["settled", "refused", "expired", "refunded", "stuck"];
|
|
2366
|
+
var RFQ_REFUSAL_ERROR_CODES = [
|
|
2367
|
+
"amount_side_unsupported",
|
|
2368
|
+
"exact_out_unsupported",
|
|
2369
|
+
"invalid_amount",
|
|
2370
|
+
"invalid_payout_address",
|
|
2371
|
+
"invalid_refund_address",
|
|
2372
|
+
"invoice_amount_mismatch",
|
|
2373
|
+
"invoice_cltv_too_large",
|
|
2374
|
+
"invoice_malformed",
|
|
2375
|
+
"invoice_missing_amount",
|
|
2376
|
+
"invoice_missing_network",
|
|
2377
|
+
"invoice_missing_payment_hash",
|
|
2378
|
+
"invoice_missing_timestamp",
|
|
2379
|
+
"invoice_mixed_case",
|
|
2380
|
+
"invoice_sub_satoshi_amount",
|
|
2381
|
+
"invoice_too_long",
|
|
2382
|
+
"invoice_wrong_network"
|
|
2383
|
+
];
|
|
2384
|
+
var isRfqRefusalErrorCode = (value) => typeof value === "string" && RFQ_REFUSAL_ERROR_CODES.includes(value);
|
|
2385
|
+
var REFUSAL_FIELDS = /* @__PURE__ */ new Set([
|
|
2386
|
+
"amount",
|
|
2387
|
+
"amount_side",
|
|
2388
|
+
"profile.invoice",
|
|
2389
|
+
"profile.payout_address",
|
|
2390
|
+
"profile.refund_address"
|
|
2391
|
+
]);
|
|
2392
|
+
var REFUSAL_UNITS = /* @__PURE__ */ new Set(["blocks", "characters", "sats"]);
|
|
2393
|
+
var safeInteger = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
2394
|
+
var safeRefusalDetail = (detail) => {
|
|
2395
|
+
if (!isRfqRefusalErrorCode(detail.errorCode)) return {};
|
|
2396
|
+
return {
|
|
2397
|
+
errorCode: detail.errorCode,
|
|
2398
|
+
field: typeof detail.field === "string" && REFUSAL_FIELDS.has(detail.field) ? detail.field : void 0,
|
|
2399
|
+
actual: safeInteger(detail.actual),
|
|
2400
|
+
expected: safeInteger(detail.expected),
|
|
2401
|
+
limit: safeInteger(detail.limit),
|
|
2402
|
+
unit: typeof detail.unit === "string" && REFUSAL_UNITS.has(detail.unit) ? detail.unit : void 0
|
|
2403
|
+
};
|
|
2404
|
+
};
|
|
2405
|
+
var refusalMessage = (reason, detail) => {
|
|
2406
|
+
if (!detail.errorCode) return `solver refused: ${reason}`;
|
|
2407
|
+
const where = detail.field ? ` at ${detail.field}` : "";
|
|
2408
|
+
const unit = detail.unit ? ` ${detail.unit}` : "";
|
|
2409
|
+
if (detail.actual !== void 0 && detail.limit !== void 0) {
|
|
2410
|
+
return `solver refused: ${reason} (${detail.errorCode}${where}: ${detail.actual}${unit}, limit ${detail.limit})`;
|
|
2411
|
+
}
|
|
2412
|
+
if (detail.actual !== void 0 && detail.expected !== void 0) {
|
|
2413
|
+
return `solver refused: ${reason} (${detail.errorCode}${where}: ${detail.actual}${unit}, expected ${detail.expected})`;
|
|
2414
|
+
}
|
|
2415
|
+
return `solver refused: ${reason} (${detail.errorCode}${where})`;
|
|
2416
|
+
};
|
|
1958
2417
|
var SwapRefusal = class extends Error {
|
|
1959
2418
|
reason;
|
|
1960
2419
|
rfqId;
|
|
1961
|
-
|
|
1962
|
-
|
|
2420
|
+
errorCode;
|
|
2421
|
+
field;
|
|
2422
|
+
actual;
|
|
2423
|
+
expected;
|
|
2424
|
+
limit;
|
|
2425
|
+
unit;
|
|
2426
|
+
constructor(reason, rfqId, detail = {}) {
|
|
2427
|
+
const safeDetail = safeRefusalDetail(detail);
|
|
2428
|
+
super(refusalMessage(reason, safeDetail));
|
|
1963
2429
|
this.name = "SwapRefusal";
|
|
1964
2430
|
this.reason = reason;
|
|
1965
2431
|
this.rfqId = rfqId;
|
|
2432
|
+
this.errorCode = safeDetail.errorCode;
|
|
2433
|
+
this.field = safeDetail.field;
|
|
2434
|
+
this.actual = safeDetail.actual;
|
|
2435
|
+
this.expected = safeDetail.expected;
|
|
2436
|
+
this.limit = safeDetail.limit;
|
|
2437
|
+
this.unit = safeDetail.unit;
|
|
1966
2438
|
}
|
|
1967
2439
|
};
|
|
1968
2440
|
var AddressMismatch = class extends Error {
|
|
@@ -2081,8 +2553,8 @@ var assertFundable = (input) => {
|
|
|
2081
2553
|
fail("max_fee_out_of_range", `maxFee.sats must be a non-negative integer, got ${sats}`);
|
|
2082
2554
|
}
|
|
2083
2555
|
const legs = input.quote.pair.split("->");
|
|
2084
|
-
const
|
|
2085
|
-
const sameAsset = legs.length === 2 &&
|
|
2556
|
+
const assetOf2 = (leg) => leg.slice(leg.indexOf(":") + 1);
|
|
2557
|
+
const sameAsset = legs.length === 2 && assetOf2(legs[0]) === assetOf2(legs[1]);
|
|
2086
2558
|
if (!sameAsset && referenceRate === void 0) {
|
|
2087
2559
|
fail(
|
|
2088
2560
|
"fee_gate_unavailable",
|
|
@@ -2130,7 +2602,20 @@ var assertFundable = (input) => {
|
|
|
2130
2602
|
};
|
|
2131
2603
|
var expectQuote = (payload, rfqId, requestedPair) => {
|
|
2132
2604
|
const p = payload;
|
|
2133
|
-
if (p?.type === "rfq_refusal")
|
|
2605
|
+
if (p?.type === "rfq_refusal") {
|
|
2606
|
+
throw new SwapRefusal(
|
|
2607
|
+
p.reason ?? "unknown",
|
|
2608
|
+
p.rfq_id ?? rfqId,
|
|
2609
|
+
safeRefusalDetail({
|
|
2610
|
+
errorCode: p.error_code,
|
|
2611
|
+
field: p.field,
|
|
2612
|
+
actual: p.actual,
|
|
2613
|
+
expected: p.expected,
|
|
2614
|
+
limit: p.limit,
|
|
2615
|
+
unit: p.unit
|
|
2616
|
+
})
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2134
2619
|
if (p?.type !== "rfq_quote" || p.rfq_id !== rfqId) {
|
|
2135
2620
|
throw new Error(`unexpected reply: ${p?.type ?? "no payload"}`);
|
|
2136
2621
|
}
|
|
@@ -2283,7 +2768,7 @@ function lightningSendVtxoScript(params) {
|
|
|
2283
2768
|
type: "seconds",
|
|
2284
2769
|
value: BigInt(value)
|
|
2285
2770
|
});
|
|
2286
|
-
return new
|
|
2771
|
+
return new import_sdk10.VHTLC.ScriptV2({
|
|
2287
2772
|
sender: params.senderPubkey,
|
|
2288
2773
|
receiver: params.solverPubkey,
|
|
2289
2774
|
server: params.serverPubkey,
|
|
@@ -2304,10 +2789,10 @@ function lightningSendVtxoScript(params) {
|
|
|
2304
2789
|
}
|
|
2305
2790
|
async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
2306
2791
|
const rfqId = params.rfqId ?? newRfqId();
|
|
2307
|
-
const secrets = await (0,
|
|
2792
|
+
const secrets = await (0, import_sdk11.provisionRefundKey)(wallet);
|
|
2308
2793
|
const senderPubkey = secrets.pubkey;
|
|
2309
2794
|
const refundAddress = secrets.address;
|
|
2310
|
-
const info = await new
|
|
2795
|
+
const info = await new import_sdk10.RestArkProvider(arkServerUrl).getInfo();
|
|
2311
2796
|
const quote = await transport.requestQuote(
|
|
2312
2797
|
lightningSendRequest({ rfqId, invoice: params.invoice.raw, refundAddress, senderPubkey })
|
|
2313
2798
|
);
|
|
@@ -2328,16 +2813,16 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
|
2328
2813
|
`quote from_amount ${quote.from_amount} is below the invoice amount \u2014 a negative spread is not a quote`
|
|
2329
2814
|
);
|
|
2330
2815
|
}
|
|
2331
|
-
const serverPubkey = (0,
|
|
2332
|
-
const network = (0,
|
|
2816
|
+
const serverPubkey = (0, import_sdk10.toXOnly)(import_base10.hex.decode(info.signerPubkey), "ark signer key");
|
|
2817
|
+
const network = (0, import_sdk10.getNetwork)(info.network);
|
|
2333
2818
|
const treeParams = {
|
|
2334
|
-
solverPubkey: (0,
|
|
2819
|
+
solverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(quote.solver_pubkey), "solver key"),
|
|
2335
2820
|
refundLocktime: quote.refund_locktime,
|
|
2336
2821
|
serverPubkey,
|
|
2337
2822
|
paymentHash: params.invoice.paymentHash,
|
|
2338
2823
|
claimDelay: unilateralClaimDelay(Number(info.unilateralExitDelay)),
|
|
2339
|
-
emulatorPubkey: (0,
|
|
2340
|
-
import_base10.hex.decode((0,
|
|
2824
|
+
emulatorPubkey: (0, import_sdk10.toXOnly)(
|
|
2825
|
+
import_base10.hex.decode((0, import_sdk10.resolveEmulatorPubkey)(network, params.emulatorPubkey)),
|
|
2341
2826
|
"emulator signer key"
|
|
2342
2827
|
),
|
|
2343
2828
|
senderPubkey,
|
|
@@ -2440,7 +2925,7 @@ function deriveOnchainSend(input) {
|
|
|
2440
2925
|
throw new Error("onchain-send quote is missing a binding field");
|
|
2441
2926
|
}
|
|
2442
2927
|
const treeParams = {
|
|
2443
|
-
solverPubkey: (0,
|
|
2928
|
+
solverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(quote.solver_pubkey), "solver key"),
|
|
2444
2929
|
refundLocktime,
|
|
2445
2930
|
serverPubkey: input.serverPubkey,
|
|
2446
2931
|
paymentHash: input.paymentHash,
|
|
@@ -2448,7 +2933,7 @@ function deriveOnchainSend(input) {
|
|
|
2448
2933
|
emulatorPubkey: input.emulatorPubkey,
|
|
2449
2934
|
senderPubkey: input.senderPubkey,
|
|
2450
2935
|
receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
|
|
2451
|
-
refundPkScript:
|
|
2936
|
+
refundPkScript: import_sdk10.ArkAddress.decode(input.refundAddress).pkScript
|
|
2452
2937
|
};
|
|
2453
2938
|
const { script, address } = matchQuotedLockup(
|
|
2454
2939
|
quote,
|
|
@@ -2459,7 +2944,7 @@ function deriveOnchainSend(input) {
|
|
|
2459
2944
|
const htlcParams = {
|
|
2460
2945
|
paymentHash: input.paymentHash,
|
|
2461
2946
|
claimKey: input.payoutPubkey,
|
|
2462
|
-
refundKey: (0,
|
|
2947
|
+
refundKey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(htlcPubkey), "solver L1 htlc key"),
|
|
2463
2948
|
refundLocktime: htlcLocktime
|
|
2464
2949
|
};
|
|
2465
2950
|
const htlc = onchainHtlcScript(htlcParams, input.l1Network);
|
|
@@ -2478,7 +2963,7 @@ function deriveOnchainSend(input) {
|
|
|
2478
2963
|
}
|
|
2479
2964
|
async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
|
|
2480
2965
|
const rfqId = params.rfqId ?? newRfqId();
|
|
2481
|
-
const secrets = await (0,
|
|
2966
|
+
const secrets = await (0, import_sdk11.provisionClaimSecret)(wallet, { preimage: params.preimage });
|
|
2482
2967
|
if (secrets.mustPersistPreimage) {
|
|
2483
2968
|
console.warn(
|
|
2484
2969
|
"[swap] this swap's preimage cannot be re-derived from the seed and MUST be persisted with the record before funding"
|
|
@@ -2487,7 +2972,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
|
|
|
2487
2972
|
const paymentHash = import_base10.hex.encode(secrets.paymentHash);
|
|
2488
2973
|
const senderPubkey = secrets.pubkey;
|
|
2489
2974
|
const [info, refundAddress] = await Promise.all([
|
|
2490
|
-
new
|
|
2975
|
+
new import_sdk10.RestArkProvider(arkServerUrl).getInfo(),
|
|
2491
2976
|
wallet.getAddress()
|
|
2492
2977
|
]);
|
|
2493
2978
|
const quote = await transport.requestQuote(
|
|
@@ -2502,14 +2987,14 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
|
|
|
2502
2987
|
})
|
|
2503
2988
|
);
|
|
2504
2989
|
assertQuotedAmount(quote, params.amountSide, params.amount);
|
|
2505
|
-
const network = (0,
|
|
2990
|
+
const network = (0, import_sdk10.getNetwork)(info.network);
|
|
2506
2991
|
const derived = deriveOnchainSend({
|
|
2507
2992
|
quote,
|
|
2508
2993
|
paymentHash,
|
|
2509
2994
|
payoutPubkey: params.payoutPubkey,
|
|
2510
|
-
serverPubkey: (0,
|
|
2511
|
-
emulatorPubkey: (0,
|
|
2512
|
-
import_base10.hex.decode((0,
|
|
2995
|
+
serverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(info.signerPubkey), "ark signer key"),
|
|
2996
|
+
emulatorPubkey: (0, import_sdk10.toXOnly)(
|
|
2997
|
+
import_base10.hex.decode((0, import_sdk10.resolveEmulatorPubkey)(network, params.emulatorPubkey)),
|
|
2513
2998
|
"emulator signer key"
|
|
2514
2999
|
),
|
|
2515
3000
|
claimDelay: unilateralClaimDelay(Number(info.unilateralExitDelay)),
|
|
@@ -2620,7 +3105,7 @@ function receiveVtxoScript(params) {
|
|
|
2620
3105
|
type: "seconds",
|
|
2621
3106
|
value: BigInt(value)
|
|
2622
3107
|
});
|
|
2623
|
-
return new
|
|
3108
|
+
return new import_sdk10.VHTLC.ScriptV2({
|
|
2624
3109
|
sender: params.solverPubkey,
|
|
2625
3110
|
receiver: params.payoutPubkey,
|
|
2626
3111
|
server: params.serverPubkey,
|
|
@@ -2649,7 +3134,7 @@ function deriveLightningReceive(input) {
|
|
|
2649
3134
|
throw new Error("lightning-receive quote is missing a binding field");
|
|
2650
3135
|
}
|
|
2651
3136
|
const treeParams = {
|
|
2652
|
-
solverPubkey: (0,
|
|
3137
|
+
solverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(quote.solver_pubkey), "solver key"),
|
|
2653
3138
|
refundLocktime,
|
|
2654
3139
|
serverPubkey: input.serverPubkey,
|
|
2655
3140
|
paymentHash: input.paymentHash,
|
|
@@ -2657,7 +3142,7 @@ function deriveLightningReceive(input) {
|
|
|
2657
3142
|
emulatorPubkey: input.emulatorPubkey,
|
|
2658
3143
|
solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
|
|
2659
3144
|
payoutPubkey: input.payoutPubkey,
|
|
2660
|
-
payoutPkScript:
|
|
3145
|
+
payoutPkScript: import_sdk10.ArkAddress.decode(input.payoutAddress).pkScript
|
|
2661
3146
|
};
|
|
2662
3147
|
const matched = matchQuotedLockup(
|
|
2663
3148
|
quote,
|
|
@@ -2679,7 +3164,7 @@ function deriveLightningReceive(input) {
|
|
|
2679
3164
|
}
|
|
2680
3165
|
async function requestLightningReceive(wallet, arkServerUrl, transport, params) {
|
|
2681
3166
|
const rfqId = params.rfqId ?? newRfqId();
|
|
2682
|
-
const secrets = await (0,
|
|
3167
|
+
const secrets = await (0, import_sdk11.provisionClaimSecret)(wallet);
|
|
2683
3168
|
if (secrets.mustPersistPreimage) {
|
|
2684
3169
|
console.warn(
|
|
2685
3170
|
"[swap] this swap's preimage cannot be re-derived from the seed and MUST be persisted with the record before paying"
|
|
@@ -2689,7 +3174,7 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
|
|
|
2689
3174
|
const paymentHash = import_base10.hex.encode(secrets.paymentHash);
|
|
2690
3175
|
const payoutPubkey = secrets.pubkey;
|
|
2691
3176
|
const [info, payoutAddress] = await Promise.all([
|
|
2692
|
-
new
|
|
3177
|
+
new import_sdk10.RestArkProvider(arkServerUrl).getInfo(),
|
|
2693
3178
|
wallet.getAddress()
|
|
2694
3179
|
]);
|
|
2695
3180
|
const claimPacket = params.covclaimdPubkey ? await sealClaimPacket({
|
|
@@ -2708,15 +3193,15 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
|
|
|
2708
3193
|
})
|
|
2709
3194
|
);
|
|
2710
3195
|
assertQuotedAmount(quote, params.amountSide, params.amount);
|
|
2711
|
-
const network = (0,
|
|
3196
|
+
const network = (0, import_sdk10.getNetwork)(info.network);
|
|
2712
3197
|
const derived = deriveLightningReceive({
|
|
2713
3198
|
quote,
|
|
2714
3199
|
paymentHash,
|
|
2715
3200
|
payoutPubkey,
|
|
2716
3201
|
payoutAddress,
|
|
2717
|
-
serverPubkey: (0,
|
|
2718
|
-
emulatorPubkey: (0,
|
|
2719
|
-
import_base10.hex.decode((0,
|
|
3202
|
+
serverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(info.signerPubkey), "ark signer key"),
|
|
3203
|
+
emulatorPubkey: (0, import_sdk10.toXOnly)(
|
|
3204
|
+
import_base10.hex.decode((0, import_sdk10.resolveEmulatorPubkey)(network, params.emulatorPubkey)),
|
|
2720
3205
|
"emulator signer key"
|
|
2721
3206
|
),
|
|
2722
3207
|
claimDelay: unilateralClaimDelay(Number(info.unilateralExitDelay)),
|
|
@@ -2764,7 +3249,7 @@ function deriveOnchainReceive(input) {
|
|
|
2764
3249
|
throw new Error("onchain-receive quote is missing a binding field");
|
|
2765
3250
|
}
|
|
2766
3251
|
const treeParams = {
|
|
2767
|
-
solverPubkey: (0,
|
|
3252
|
+
solverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(quote.solver_pubkey), "solver key"),
|
|
2768
3253
|
refundLocktime,
|
|
2769
3254
|
serverPubkey: input.serverPubkey,
|
|
2770
3255
|
paymentHash: input.paymentHash,
|
|
@@ -2772,7 +3257,7 @@ function deriveOnchainReceive(input) {
|
|
|
2772
3257
|
emulatorPubkey: input.emulatorPubkey,
|
|
2773
3258
|
solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
|
|
2774
3259
|
payoutPubkey: input.payoutPubkey,
|
|
2775
|
-
payoutPkScript:
|
|
3260
|
+
payoutPkScript: import_sdk10.ArkAddress.decode(input.payoutAddress).pkScript
|
|
2776
3261
|
};
|
|
2777
3262
|
const { script, address } = matchQuotedLockup(
|
|
2778
3263
|
quote,
|
|
@@ -2783,7 +3268,7 @@ function deriveOnchainReceive(input) {
|
|
|
2783
3268
|
const htlc = onchainHtlcScript(
|
|
2784
3269
|
{
|
|
2785
3270
|
paymentHash: input.paymentHash,
|
|
2786
|
-
claimKey: (0,
|
|
3271
|
+
claimKey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(claimPubkey), "solver L1 claim key"),
|
|
2787
3272
|
refundKey: input.refundPubkey,
|
|
2788
3273
|
refundLocktime: htlcLocktime
|
|
2789
3274
|
},
|
|
@@ -2802,7 +3287,7 @@ function deriveOnchainReceive(input) {
|
|
|
2802
3287
|
}
|
|
2803
3288
|
async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
|
|
2804
3289
|
const rfqId = params.rfqId ?? newRfqId();
|
|
2805
|
-
const secrets = await (0,
|
|
3290
|
+
const secrets = await (0, import_sdk11.provisionClaimSecret)(wallet);
|
|
2806
3291
|
if (secrets.mustPersistPreimage) {
|
|
2807
3292
|
console.warn(
|
|
2808
3293
|
"[swap] this swap's preimage cannot be re-derived from the seed and MUST be persisted with the record before funding"
|
|
@@ -2812,7 +3297,7 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
|
|
|
2812
3297
|
const paymentHash = import_base10.hex.encode(secrets.paymentHash);
|
|
2813
3298
|
const payoutPubkey = secrets.pubkey;
|
|
2814
3299
|
const [info, payoutAddress] = await Promise.all([
|
|
2815
|
-
new
|
|
3300
|
+
new import_sdk10.RestArkProvider(arkServerUrl).getInfo(),
|
|
2816
3301
|
wallet.getAddress()
|
|
2817
3302
|
]);
|
|
2818
3303
|
const claimPacket = params.covclaimdPubkey ? await sealClaimPacket({
|
|
@@ -2832,16 +3317,16 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
|
|
|
2832
3317
|
})
|
|
2833
3318
|
);
|
|
2834
3319
|
assertQuotedAmount(quote, params.amountSide, params.amount);
|
|
2835
|
-
const network = (0,
|
|
3320
|
+
const network = (0, import_sdk10.getNetwork)(info.network);
|
|
2836
3321
|
const derived = deriveOnchainReceive({
|
|
2837
3322
|
quote,
|
|
2838
3323
|
paymentHash,
|
|
2839
3324
|
payoutPubkey,
|
|
2840
3325
|
payoutAddress,
|
|
2841
3326
|
refundPubkey: params.refundPubkey,
|
|
2842
|
-
serverPubkey: (0,
|
|
2843
|
-
emulatorPubkey: (0,
|
|
2844
|
-
import_base10.hex.decode((0,
|
|
3327
|
+
serverPubkey: (0, import_sdk10.toXOnly)(import_base10.hex.decode(info.signerPubkey), "ark signer key"),
|
|
3328
|
+
emulatorPubkey: (0, import_sdk10.toXOnly)(
|
|
3329
|
+
import_base10.hex.decode((0, import_sdk10.resolveEmulatorPubkey)(network, params.emulatorPubkey)),
|
|
2845
3330
|
"emulator signer key"
|
|
2846
3331
|
),
|
|
2847
3332
|
claimDelay: unilateralClaimDelay(Number(info.unilateralExitDelay)),
|
|
@@ -2961,7 +3446,7 @@ var solverRendezvous = (markets, payoutCorridor, amountSats, fallbackEmulatorPub
|
|
|
2961
3446
|
};
|
|
2962
3447
|
|
|
2963
3448
|
// src/payment/solverOnchain.ts
|
|
2964
|
-
var
|
|
3449
|
+
var import_sdk12 = require("@arkade-os/sdk");
|
|
2965
3450
|
var SOLVER_ONCHAIN_RAIL = "solver-onchain";
|
|
2966
3451
|
var solverOnchainRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "onchain", amountSats, fallbackEmulatorPubkey);
|
|
2967
3452
|
function solverOnchainRail(deps) {
|
|
@@ -2972,24 +3457,24 @@ function solverOnchainRail(deps) {
|
|
|
2972
3457
|
};
|
|
2973
3458
|
return {
|
|
2974
3459
|
id: SOLVER_ONCHAIN_RAIL,
|
|
2975
|
-
match: (req) => (0,
|
|
3460
|
+
match: (req) => (0, import_sdk12.btcTarget)(req.raw) !== void 0,
|
|
2976
3461
|
available: async (req) => {
|
|
2977
|
-
if ((0,
|
|
2978
|
-
const address = (0,
|
|
3462
|
+
if ((0, import_sdk12.assetsOf)(req).length > 0) return false;
|
|
3463
|
+
const address = (0, import_sdk12.btcTarget)(req.raw);
|
|
2979
3464
|
if (!address) return false;
|
|
2980
3465
|
try {
|
|
2981
3466
|
l1ScriptForAddress(address, deps.l1Network);
|
|
2982
3467
|
} catch {
|
|
2983
3468
|
return false;
|
|
2984
3469
|
}
|
|
2985
|
-
const amount = (0,
|
|
3470
|
+
const amount = (0, import_sdk12.tryResolveSendAmount)(req.raw, req.amount);
|
|
2986
3471
|
if (amount === void 0) return false;
|
|
2987
3472
|
return await rendezvousFor(amount) !== void 0;
|
|
2988
3473
|
},
|
|
2989
3474
|
quote: async (req, ctx) => {
|
|
2990
|
-
const address = (0,
|
|
2991
|
-
(0,
|
|
2992
|
-
const amount = (0,
|
|
3475
|
+
const address = (0, import_sdk12.btcTarget)(req.raw);
|
|
3476
|
+
(0, import_sdk12.assertNoAssets)(SOLVER_ONCHAIN_RAIL, req);
|
|
3477
|
+
const amount = (0, import_sdk12.resolveSendAmount)(SOLVER_ONCHAIN_RAIL, req.raw, req.amount);
|
|
2993
3478
|
const payoutPkScript = l1ScriptForAddress(address, deps.l1Network);
|
|
2994
3479
|
const rendezvous = await rendezvousFor(amount);
|
|
2995
3480
|
if (!rendezvous) {
|
|
@@ -3017,6 +3502,7 @@ function solverOnchainRail(deps) {
|
|
|
3017
3502
|
amount,
|
|
3018
3503
|
fee: swap.fundAmount - amount,
|
|
3019
3504
|
total: swap.fundAmount,
|
|
3505
|
+
validUntil: swap.quote.valid_until,
|
|
3020
3506
|
meta: {
|
|
3021
3507
|
rfqId: swap.rfqId,
|
|
3022
3508
|
validUntil: swap.quote.valid_until,
|
|
@@ -3027,7 +3513,7 @@ function solverOnchainRail(deps) {
|
|
|
3027
3513
|
// not knowable now, so `amount` is the payout, not the net.
|
|
3028
3514
|
claimFeeDeductedFromPayout: true
|
|
3029
3515
|
},
|
|
3030
|
-
send: async () => (0,
|
|
3516
|
+
send: async () => (0, import_sdk12.makeHandle)(SOLVER_ONCHAIN_RAIL, async (emit) => {
|
|
3031
3517
|
assertFundable({
|
|
3032
3518
|
quote: swap.quote,
|
|
3033
3519
|
now: Math.floor(Date.now() / 1e3),
|
|
@@ -3065,10 +3551,10 @@ function solverOnchainRail(deps) {
|
|
|
3065
3551
|
}
|
|
3066
3552
|
|
|
3067
3553
|
// src/payment/solverLightning.ts
|
|
3068
|
-
var
|
|
3554
|
+
var import_sdk13 = require("@arkade-os/sdk");
|
|
3069
3555
|
var SOLVER_LIGHTNING_RAIL = "solver-lightning";
|
|
3070
3556
|
var factsOf = (raw, decode, now) => {
|
|
3071
|
-
const invoice = (0,
|
|
3557
|
+
const invoice = (0, import_sdk13.invoiceTarget)(raw);
|
|
3072
3558
|
if (!invoice) return void 0;
|
|
3073
3559
|
let facts;
|
|
3074
3560
|
try {
|
|
@@ -3085,16 +3571,16 @@ function solverLightningRail(deps) {
|
|
|
3085
3571
|
const rendezvousFor = async (amountSats) => solverLightningRendezvous(await deps.discover(), amountSats, deps.fallbackEmulatorPubkey);
|
|
3086
3572
|
return {
|
|
3087
3573
|
id: SOLVER_LIGHTNING_RAIL,
|
|
3088
|
-
match: (req) => (0,
|
|
3574
|
+
match: (req) => (0, import_sdk13.invoiceTarget)(req.raw) !== void 0,
|
|
3089
3575
|
available: async (req) => {
|
|
3090
|
-
if ((0,
|
|
3576
|
+
if ((0, import_sdk13.assetsOf)(req).length > 0) return false;
|
|
3091
3577
|
const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
|
|
3092
3578
|
if (!facts) return false;
|
|
3093
3579
|
if (req.amount !== void 0 && req.amount !== facts.amountSats) return false;
|
|
3094
3580
|
return await rendezvousFor(facts.amountSats) !== void 0;
|
|
3095
3581
|
},
|
|
3096
3582
|
quote: async (req, ctx) => {
|
|
3097
|
-
(0,
|
|
3583
|
+
(0, import_sdk13.assertNoAssets)(SOLVER_LIGHTNING_RAIL, req);
|
|
3098
3584
|
const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
|
|
3099
3585
|
if (!facts) {
|
|
3100
3586
|
throw new Error(
|
|
@@ -3127,6 +3613,9 @@ function solverLightningRail(deps) {
|
|
|
3127
3613
|
amount: facts.amountSats,
|
|
3128
3614
|
fee: swap.fundAmount - facts.amountSats,
|
|
3129
3615
|
total: swap.fundAmount,
|
|
3616
|
+
// The invoice can lapse before the quote does; the earlier bound
|
|
3617
|
+
// is the one a holder must observe.
|
|
3618
|
+
validUntil: Math.min(swap.quote.valid_until, facts.expiresAt),
|
|
3130
3619
|
meta: {
|
|
3131
3620
|
rfqId: swap.rfqId,
|
|
3132
3621
|
validUntil: swap.quote.valid_until,
|
|
@@ -3134,7 +3623,7 @@ function solverLightningRail(deps) {
|
|
|
3134
3623
|
invoiceExpiresAt: facts.expiresAt,
|
|
3135
3624
|
solverPubkey: rendezvous.solverPubkey
|
|
3136
3625
|
},
|
|
3137
|
-
send: async () => (0,
|
|
3626
|
+
send: async () => (0, import_sdk13.makeHandle)(SOLVER_LIGHTNING_RAIL, async (emit) => {
|
|
3138
3627
|
assertFundable({
|
|
3139
3628
|
quote: swap.quote,
|
|
3140
3629
|
invoiceExpiresAt: facts.expiresAt,
|
|
@@ -3168,16 +3657,17 @@ function solverLightningRail(deps) {
|
|
|
3168
3657
|
}
|
|
3169
3658
|
|
|
3170
3659
|
// src/payment/crossAsset.ts
|
|
3171
|
-
var
|
|
3660
|
+
var import_sdk14 = require("@arkade-os/sdk");
|
|
3172
3661
|
var CROSS_ASSET_RAIL = "cross-asset";
|
|
3173
3662
|
var DEFAULT_CARRIER_SATS = 330;
|
|
3174
3663
|
var parseAssetId = (assetId) => {
|
|
3175
3664
|
try {
|
|
3176
|
-
return
|
|
3665
|
+
return import_sdk14.asset.AssetId.fromString(assetId);
|
|
3177
3666
|
} catch {
|
|
3178
3667
|
return void 0;
|
|
3179
3668
|
}
|
|
3180
3669
|
};
|
|
3670
|
+
var marketLabel = (market) => `${market.base_asset.ticker || market.base_asset.id}/${market.quote_asset.ticker || market.quote_asset.id}`;
|
|
3181
3671
|
function crossAssetRail(deps) {
|
|
3182
3672
|
const planFor = async (asset3) => {
|
|
3183
3673
|
const markets = await deps.discover();
|
|
@@ -3190,9 +3680,9 @@ function crossAssetRail(deps) {
|
|
|
3190
3680
|
};
|
|
3191
3681
|
return {
|
|
3192
3682
|
id: CROSS_ASSET_RAIL,
|
|
3193
|
-
match: (req) => (0,
|
|
3683
|
+
match: (req) => (0, import_sdk14.arkTarget)(req.raw) !== void 0,
|
|
3194
3684
|
available: async (req) => {
|
|
3195
|
-
const assets = (0,
|
|
3685
|
+
const assets = (0, import_sdk14.assetsOf)(req);
|
|
3196
3686
|
if (assets.length !== 1) return false;
|
|
3197
3687
|
const [asset3] = assets;
|
|
3198
3688
|
if (asset3.assetId === BTC_ASSET_ID) return false;
|
|
@@ -3202,8 +3692,8 @@ function crossAssetRail(deps) {
|
|
|
3202
3692
|
return planned !== void 0 && planned.error === void 0;
|
|
3203
3693
|
},
|
|
3204
3694
|
quote: async (req, ctx) => {
|
|
3205
|
-
const payTo = (0,
|
|
3206
|
-
const asset3 = (0,
|
|
3695
|
+
const payTo = (0, import_sdk14.arkTarget)(req.raw);
|
|
3696
|
+
const asset3 = (0, import_sdk14.resolveAssetAmount)(CROSS_ASSET_RAIL, req);
|
|
3207
3697
|
const wantAsset = parseAssetId(asset3.assetId);
|
|
3208
3698
|
if (!wantAsset) {
|
|
3209
3699
|
throw new Error(
|
|
@@ -3240,11 +3730,11 @@ function crossAssetRail(deps) {
|
|
|
3240
3730
|
spent: { assetId: BTC_ASSET_ID, amount: plan.deposit.atomic }
|
|
3241
3731
|
},
|
|
3242
3732
|
meta: {
|
|
3243
|
-
market: plan.market
|
|
3733
|
+
market: marketLabel(plan.market),
|
|
3244
3734
|
priceDisplay: plan.priceDisplay,
|
|
3245
3735
|
give: plan.give
|
|
3246
3736
|
},
|
|
3247
|
-
send: async () => (0,
|
|
3737
|
+
send: async () => (0, import_sdk14.makeHandle)(CROSS_ASSET_RAIL, async (emit) => {
|
|
3248
3738
|
const offer = await createOffer(ctx.wallet, deps.arkServerUrl, {
|
|
3249
3739
|
wantAmount: asset3.amount,
|
|
3250
3740
|
wantAsset,
|
|
@@ -3293,7 +3783,7 @@ function crossAssetRail(deps) {
|
|
|
3293
3783
|
var import_base13 = require("@scure/base");
|
|
3294
3784
|
var import_legacy3 = require("@noble/hashes/legacy.js");
|
|
3295
3785
|
var import_sha24 = require("@noble/hashes/sha2.js");
|
|
3296
|
-
var
|
|
3786
|
+
var import_sdk15 = require("@arkade-os/sdk");
|
|
3297
3787
|
var CovclaimdRevealError = class extends Error {
|
|
3298
3788
|
name = "CovclaimdRevealError";
|
|
3299
3789
|
status;
|
|
@@ -3397,7 +3887,7 @@ function revealFieldsFromScript(script) {
|
|
|
3397
3887
|
}
|
|
3398
3888
|
async function revealClaimPacket(client, input) {
|
|
3399
3889
|
const fields = revealFieldsFromScript(input.script);
|
|
3400
|
-
if (import_base13.hex.encode(
|
|
3890
|
+
if (import_base13.hex.encode(import_sdk15.ArkAddress.decode(input.address).pkScript) !== import_base13.hex.encode(input.script.pkScript)) {
|
|
3401
3891
|
throw new Error("address does not belong to this covenant");
|
|
3402
3892
|
}
|
|
3403
3893
|
if (import_base13.hex.encode((0, import_legacy3.ripemd160)((0, import_sha24.sha256)(input.preimage))) !== import_base13.hex.encode(input.script.options.preimageHash)) {
|
|
@@ -3415,12 +3905,12 @@ async function revealClaimPacket(client, input) {
|
|
|
3415
3905
|
var import_base15 = require("@scure/base");
|
|
3416
3906
|
var import_legacy4 = require("@noble/hashes/legacy.js");
|
|
3417
3907
|
var import_sha26 = require("@noble/hashes/sha2.js");
|
|
3418
|
-
var
|
|
3908
|
+
var import_sdk17 = require("@arkade-os/sdk");
|
|
3419
3909
|
|
|
3420
3910
|
// src/refund.ts
|
|
3421
3911
|
var import_base14 = require("@scure/base");
|
|
3422
3912
|
var import_sha25 = require("@noble/hashes/sha2.js");
|
|
3423
|
-
var
|
|
3913
|
+
var import_sdk16 = require("@arkade-os/sdk");
|
|
3424
3914
|
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3425
3915
|
var isRfqTerminal = (state) => RFQ_TERMINAL_STATES.includes(state);
|
|
3426
3916
|
var RFQ_RESOLVED_STATES = ["settled", "refunded"];
|
|
@@ -3495,14 +3985,14 @@ async function findLockupVtxos(indexer, swapPkScript) {
|
|
|
3495
3985
|
}
|
|
3496
3986
|
var hashesTo = (candidate, paymentHash) => import_base14.hex.encode((0, import_sha25.sha256)(candidate)) === paymentHash;
|
|
3497
3987
|
var candidateWitnessItems = (tx, inputIndex) => [
|
|
3498
|
-
...(0,
|
|
3988
|
+
...(0, import_sdk16.getArkPsbtFields)(tx, inputIndex, import_sdk16.ConditionWitness).flat(),
|
|
3499
3989
|
...tx.getInput(inputIndex).finalScriptWitness ?? []
|
|
3500
3990
|
];
|
|
3501
3991
|
async function readLockupFate(indexer, input) {
|
|
3502
3992
|
const { vtxos } = await indexer.getVtxos({ scripts: [import_base14.hex.encode(input.swapPkScript)] });
|
|
3503
3993
|
const all = vtxos ?? [];
|
|
3504
3994
|
if (all.length === 0) return { fate: "unknown" };
|
|
3505
|
-
const exited = all.filter((vtxo) => vtxo.isUnrolled && !(0,
|
|
3995
|
+
const exited = all.filter((vtxo) => vtxo.isUnrolled && !(0, import_sdk16.hasTerminalSpend)(vtxo));
|
|
3506
3996
|
if (exited.length > 0) {
|
|
3507
3997
|
return {
|
|
3508
3998
|
fate: "exited",
|
|
@@ -3512,7 +4002,7 @@ async function readLockupFate(indexer, input) {
|
|
|
3512
4002
|
const spentBy = /* @__PURE__ */ new Map();
|
|
3513
4003
|
let everySpendNamed = true;
|
|
3514
4004
|
for (const vtxo of all) {
|
|
3515
|
-
if (!(0,
|
|
4005
|
+
if (!(0, import_sdk16.hasTerminalSpend)(vtxo)) return { fate: "open" };
|
|
3516
4006
|
if (vtxo.spentBy)
|
|
3517
4007
|
spentBy.set(vtxo.spentBy, {
|
|
3518
4008
|
checkpointTxid: vtxo.spentBy,
|
|
@@ -3526,7 +4016,7 @@ async function readLockupFate(indexer, input) {
|
|
|
3526
4016
|
for (const raw of txs) {
|
|
3527
4017
|
let tx;
|
|
3528
4018
|
try {
|
|
3529
|
-
tx =
|
|
4019
|
+
tx = import_sdk16.Transaction.fromPSBT(import_base14.base64.decode(raw));
|
|
3530
4020
|
} catch {
|
|
3531
4021
|
continue;
|
|
3532
4022
|
}
|
|
@@ -3563,14 +4053,14 @@ async function pushRefundWithoutReceiver(ark, input) {
|
|
|
3563
4053
|
const info = await ark.getInfo();
|
|
3564
4054
|
let serverUnrollScript;
|
|
3565
4055
|
try {
|
|
3566
|
-
serverUnrollScript =
|
|
4056
|
+
serverUnrollScript = import_sdk16.CSVMultisigTapscript.decode(import_base14.hex.decode(info.checkpointTapscript));
|
|
3567
4057
|
} catch {
|
|
3568
4058
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
3569
4059
|
}
|
|
3570
4060
|
const leaf = input.script.refundWithoutReceiver();
|
|
3571
4061
|
const tapTree = input.script.encode();
|
|
3572
4062
|
const amount = input.vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
|
|
3573
|
-
const { arkTx, checkpoints } = (0,
|
|
4063
|
+
const { arkTx, checkpoints } = (0, import_sdk16.buildOffchainTx)(
|
|
3574
4064
|
input.vtxos.map((vtxo) => ({
|
|
3575
4065
|
txid: vtxo.txid,
|
|
3576
4066
|
vout: vtxo.vout,
|
|
@@ -3586,8 +4076,8 @@ async function pushRefundWithoutReceiver(ark, input) {
|
|
|
3586
4076
|
import_base14.base64.encode(signedArkTx.toPSBT()),
|
|
3587
4077
|
checkpoints.map((c) => import_base14.base64.encode(c.toPSBT()))
|
|
3588
4078
|
);
|
|
3589
|
-
(0,
|
|
3590
|
-
const matched = (0,
|
|
4079
|
+
(0, import_sdk16.assertSubmittedArkTxid)(submitted, signedArkTx, "refundWithoutReceiver");
|
|
4080
|
+
const matched = (0, import_sdk16.matchServerCheckpoints)(
|
|
3591
4081
|
submitted.signedCheckpointTxs,
|
|
3592
4082
|
checkpoints,
|
|
3593
4083
|
"refundWithoutReceiver"
|
|
@@ -3695,14 +4185,14 @@ async function pushClaim(ark, input) {
|
|
|
3695
4185
|
const info = await ark.getInfo();
|
|
3696
4186
|
let serverUnrollScript;
|
|
3697
4187
|
try {
|
|
3698
|
-
serverUnrollScript =
|
|
4188
|
+
serverUnrollScript = import_sdk17.CSVMultisigTapscript.decode(import_base15.hex.decode(info.checkpointTapscript));
|
|
3699
4189
|
} catch {
|
|
3700
4190
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
3701
4191
|
}
|
|
3702
4192
|
const leaf = input.script.claim();
|
|
3703
4193
|
const tapTree = input.script.encode();
|
|
3704
|
-
const arkTxid = await (0,
|
|
3705
|
-
identity: (0,
|
|
4194
|
+
const arkTxid = await (0, import_sdk17.signAndSubmitOffchainTx)({
|
|
4195
|
+
identity: (0, import_sdk17.claimWithPreimageIdentity)(input.receiver, input.preimage),
|
|
3706
4196
|
provider: ark,
|
|
3707
4197
|
inputs: input.vtxos.map((vtxo) => ({
|
|
3708
4198
|
txid: vtxo.txid,
|
|
@@ -3749,7 +4239,7 @@ async function claimReceiveLockup(indexer, ark, input) {
|
|
|
3749
4239
|
}
|
|
3750
4240
|
|
|
3751
4241
|
// src/refundBlocked.ts
|
|
3752
|
-
var
|
|
4242
|
+
var import_sdk18 = require("@arkade-os/sdk");
|
|
3753
4243
|
var RefundNotLocallyPossibleError = class extends Error {
|
|
3754
4244
|
constructor(reason, message, options) {
|
|
3755
4245
|
super(message, options);
|
|
@@ -3766,16 +4256,16 @@ async function senderIdentityForSwapRecord(wallet, record) {
|
|
|
3766
4256
|
);
|
|
3767
4257
|
}
|
|
3768
4258
|
try {
|
|
3769
|
-
return await (0,
|
|
4259
|
+
return await (0, import_sdk18.contractSigner)(wallet, record.signingDescriptor);
|
|
3770
4260
|
} catch (cause) {
|
|
3771
|
-
if (cause instanceof
|
|
4261
|
+
if (cause instanceof import_sdk18.WalletCannotSignError) {
|
|
3772
4262
|
throw new RefundNotLocallyPossibleError(
|
|
3773
4263
|
"unsignable-wallet",
|
|
3774
4264
|
`this wallet holds ${record.signingDescriptor} but cannot sign with it; attach its signer`,
|
|
3775
4265
|
{ cause }
|
|
3776
4266
|
);
|
|
3777
4267
|
}
|
|
3778
|
-
if (cause instanceof
|
|
4268
|
+
if (cause instanceof import_sdk18.ForeignDescriptorError) {
|
|
3779
4269
|
throw new RefundNotLocallyPossibleError(
|
|
3780
4270
|
"foreign-descriptor",
|
|
3781
4271
|
`this wallet cannot derive ${record.signingDescriptor}; the swap was created on another wallet`,
|
|
@@ -3811,7 +4301,7 @@ function arkadeRefunder(deps) {
|
|
|
3811
4301
|
|
|
3812
4302
|
// src/swapManager.ts
|
|
3813
4303
|
var import_base16 = require("@scure/base");
|
|
3814
|
-
var
|
|
4304
|
+
var import_sdk19 = require("@arkade-os/sdk");
|
|
3815
4305
|
function nextOnchainAction(input) {
|
|
3816
4306
|
switch (input.phase.phase) {
|
|
3817
4307
|
case "unfunded":
|
|
@@ -4301,7 +4791,7 @@ var RfqSwapManager = class {
|
|
|
4301
4791
|
});
|
|
4302
4792
|
return;
|
|
4303
4793
|
}
|
|
4304
|
-
if (!(0,
|
|
4794
|
+
if (!(0, import_sdk19.isContractVtxoEvent)(event)) return;
|
|
4305
4795
|
const swap = this.byLockupScript.get(event.contractScript);
|
|
4306
4796
|
if (!swap) return;
|
|
4307
4797
|
void this.pollSwap(swap).catch(() => {
|
|
@@ -4848,7 +5338,7 @@ var errorMessage = (error) => error instanceof Error ? error.message : String(er
|
|
|
4848
5338
|
var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
|
|
4849
5339
|
|
|
4850
5340
|
// src/activity.ts
|
|
4851
|
-
var
|
|
5341
|
+
var import_sdk20 = require("@arkade-os/sdk");
|
|
4852
5342
|
var import_base17 = require("@scure/base");
|
|
4853
5343
|
var LABELS = {
|
|
4854
5344
|
lightning_send: "Lightning send",
|
|
@@ -4924,7 +5414,7 @@ async function activityInputOf(record, indexer) {
|
|
|
4924
5414
|
async function lockupTxids(indexer, record, wantFunding) {
|
|
4925
5415
|
let script;
|
|
4926
5416
|
try {
|
|
4927
|
-
script = import_base17.hex.encode(
|
|
5417
|
+
script = import_base17.hex.encode(import_sdk20.ArkAddress.decode(record.lockupAddress).pkScript);
|
|
4928
5418
|
} catch {
|
|
4929
5419
|
return [];
|
|
4930
5420
|
}
|
|
@@ -4944,6 +5434,7 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4944
5434
|
0 && (module.exports = {
|
|
4945
5435
|
ARKADE_ASSET,
|
|
4946
5436
|
ARKADE_BTC,
|
|
5437
|
+
ASSET_CARRIER_SATS,
|
|
4947
5438
|
AddressMismatch,
|
|
4948
5439
|
BTC_ASSET_ID,
|
|
4949
5440
|
CROSS_ASSET_RAIL,
|
|
@@ -4958,6 +5449,7 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4958
5449
|
LockupContractMissing,
|
|
4959
5450
|
LockupNeedsRecoveryError,
|
|
4960
5451
|
LockupRegistrationFailed,
|
|
5452
|
+
MARKET_CORRIDORS,
|
|
4961
5453
|
MAX_MIN_CONFIRMATIONS,
|
|
4962
5454
|
MIN_CLAIM_WINDOW_SECONDS,
|
|
4963
5455
|
MIN_HEADROOM_SECONDS,
|
|
@@ -4972,6 +5464,7 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4972
5464
|
PreimageNotRecoverableError,
|
|
4973
5465
|
QUOTE_OPTIONS,
|
|
4974
5466
|
REFUND_MTP_LAG_SECONDS,
|
|
5467
|
+
RFQ_REFUSAL_ERROR_CODES,
|
|
4975
5468
|
RFQ_RESOLVED_STATES,
|
|
4976
5469
|
RFQ_SWAP_RETENTION_SECONDS,
|
|
4977
5470
|
RFQ_SWAP_TERMINAL_STATES,
|
|
@@ -5015,11 +5508,14 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
5015
5508
|
discoverMarkets,
|
|
5016
5509
|
encodeOffer,
|
|
5017
5510
|
extractPreimage,
|
|
5511
|
+
fillOffer,
|
|
5018
5512
|
findLockupVtxos,
|
|
5019
5513
|
findMarket,
|
|
5020
5514
|
getAssetSwaps,
|
|
5021
5515
|
getAssetSwapsOrThrow,
|
|
5022
5516
|
httpTransport,
|
|
5517
|
+
isRfqMarket,
|
|
5518
|
+
isRfqRefusalErrorCode,
|
|
5023
5519
|
isRfqSwapTerminal,
|
|
5024
5520
|
isRfqTerminal,
|
|
5025
5521
|
l1ScriptForAddress,
|
|
@@ -5028,6 +5524,9 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
5028
5524
|
lightningSendVtxoScript,
|
|
5029
5525
|
lockupContractParams,
|
|
5030
5526
|
makeCachedFeedFetch,
|
|
5527
|
+
marketAssetId,
|
|
5528
|
+
marketCorridor,
|
|
5529
|
+
marketPairLabel,
|
|
5031
5530
|
newPreimage,
|
|
5032
5531
|
newRfqId,
|
|
5033
5532
|
nextOnchainAction,
|
|
@@ -5045,13 +5544,16 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
5045
5544
|
rebuildRfqSwap,
|
|
5046
5545
|
receiveVtxoScript,
|
|
5047
5546
|
refundIfUnresolved,
|
|
5547
|
+
registerAssetSwapRestore,
|
|
5048
5548
|
registerLockupContract,
|
|
5049
5549
|
relayTransport,
|
|
5050
5550
|
requestLightningReceive,
|
|
5051
5551
|
requestLightningSend,
|
|
5052
5552
|
requestOnchainReceive,
|
|
5053
5553
|
requestOnchainSend,
|
|
5554
|
+
restoreAssetSwapRepository,
|
|
5054
5555
|
restoreAssetSwaps,
|
|
5556
|
+
restoreOfferCoverage,
|
|
5055
5557
|
retireSettledOfferContracts,
|
|
5056
5558
|
revealClaimPacket,
|
|
5057
5559
|
revealFieldsFromScript,
|