@arkade-os/swap 0.0.10 → 0.1.0-rc.0

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 CHANGED
@@ -12,6 +12,33 @@ React Native does not, so install `react-native-get-random-values` (or `expo-cry
12
12
  it before this package. `crypto.subtle` is not used. `EventSource` and `WebSocket` are needed only
13
13
  by the watch and relay transports, both of which take an injected implementation.
14
14
 
15
+ The v2 swap client API is tracked in [V2_API.md](./V2_API.md). That document is
16
+ the package-level developer UX note for the new client surface as it lands; the
17
+ current README still documents the existing package exports and protocol
18
+ building blocks.
19
+
20
+ **The covenant-deriving entry points need a wallet and nothing else.** `createOffer`,
21
+ `requestLightningSend`, `requestLightningReceive`, `requestOnchainSend` and
22
+ `requestOnchainReceive` take their server facts from `wallet.getArkadeInfo()`: the network and
23
+ signer key, plus the unilateral-exit delay for the four `request*` calls. The wallet is the single
24
+ place that knows which server it speaks to, so there is no URL to thread through and no second
25
+ `/v1/info` round-trip *per call*. Each entrypoint still performs its own live read (deliberately —
26
+ covenant derivation requires live info and fails closed offline); a session creating many offers
27
+ pays one read per offer until the SDK grows a `CachingClientTransport`-style memo (the NArk
28
+ reference's answer), noted as follow-up on `ArkadeInfo`.
29
+
30
+ No offer entrypoint here takes a server URL. `cancelOffer` and `watchOfferSwaps` need more than
31
+ server info — cancel broadcasts the refund and falls back to the indexer for a deposit made
32
+ before contract registration existed, and the watcher reads spending transactions — so they
33
+ ask the wallet for those too: `wallet.getArkadeReader()` for chain reads and
34
+ `wallet.getArkadeBroadcaster()` for `submitTx`/`finalizeTx`. On a service-worker wallet both
35
+ are proxied to the worker, so these reads stay on the wallet's own connection.
36
+ The RFQ restore/refund/claim helpers still take provider instances. An `ArkadeReader`
37
+ satisfies their *indexer* parameter structurally — `restoreAssetSwaps` can be fed
38
+ `await wallet.getArkadeReader()` today — while `arkadeRefunder` and `claim`/`refund` also
39
+ want an ark provider (`getInfo` plus the broadcast pair), buildable from
40
+ `wallet.getArkadeInfo()` and `wallet.getArkadeBroadcaster()`.
41
+
15
42
  ## Roles
16
43
 
17
44
  Arkade Intents names two participants:
@@ -76,7 +103,7 @@ funds an offer should keep cancelling within reach.
76
103
 
77
104
  1. **`offer`** — the swap covenant itself. Two program JSONs (want-BTC / want-asset), the
78
105
  `Offer` type, the TLV wire codec (`encodeOffer`/`decodeOffer`, `OFFER_PACKET_TYPE`), address
79
- derivation (`offerVtxoScript`), and the user-side operations `createOffer`/`cancelOffer`. Identical
106
+ derivation (`offerContract`), and the user-side operations `createOffer`/`cancelOffer`. Identical
80
107
  offers always derive identical swap addresses — the program JSONs are hashed into the address,
81
108
  so their bytes are frozen (guarded by a golden test).
82
109
  2. **`markets`** — solver discovery and pricing guardrails: `discoverMarkets` (1-hour cached
@@ -128,8 +155,8 @@ Realm — since the two record types have different keys and no consumer wants t
128
155
 
129
156
  **Records are stored whole.** The SQLite and Realm backends serialize each record to **JSON** in a
130
157
  `data` column, with only `status` / `createdAt` (and an RFQ record's `state` / `updatedAt`) mapped
131
- out for querying — so a field they do not know about survives, which is what the `quote`-shaped
132
- extension in `MIGRATION.md` relies on. It is also what keeps an RFQ record's corridor `profile`
158
+ out for querying — so a field they do not know about survives, which is what a consumer's
159
+ cast-extended record relies on. It is also what keeps an RFQ record's corridor `profile`
133
160
  intact: `profile.hashlock` is a nested object holding the payment hash and any preimage material, and
134
161
  a field-mapped backend is exactly what would lose it. JSON is
135
162
  the boundary, though, and it is narrower than IndexedDB's structured clone: a `Date` in a
@@ -200,11 +227,11 @@ the rest:
200
227
 
201
228
  ```ts
202
229
  // BTC -> asset
203
- const o = await createOffer(wallet, ARK, { wantAmount: 1000n, wantAsset });
230
+ const o = await createOffer(wallet, { wantAmount: 1000n, wantAsset });
204
231
  await wallet.send({ address: o.address, amount: 1000, extensions: [o.extension] });
205
232
 
206
233
  // asset -> BTC (the sats are the VTXO carrier for the asset)
207
- const o = await createOffer(wallet, ARK, { wantAmount: 1000n, offerAsset });
234
+ const o = await createOffer(wallet, { wantAmount: 1000n, offerAsset });
208
235
  await wallet.send({
209
236
  address: o.address,
210
237
  amount: 500,
@@ -238,7 +265,7 @@ offer bytes themselves are recoverable from the funding tx if the record is lost
238
265
  ## Live status
239
266
 
240
267
  ```ts
241
- const watcher = await watchOfferSwaps({ wallet, arkServerUrl: ARK, repository, onUpdate: render });
268
+ const watcher = await watchOfferSwaps({ wallet, repository, onUpdate: render });
242
269
  // later
243
270
  watcher.stop();
244
271
  ```
@@ -260,7 +287,7 @@ repository.
260
287
  ## Cancelling: the refund path
261
288
 
262
289
  ```ts
263
- const txid = await cancelOffer(wallet, ARK, swap.offerHex, {
290
+ const txid = await cancelOffer(wallet, swap.offerHex, {
264
291
  repository,
265
292
  fundingTxid: swap.fundingTxid,
266
293
  swapAddress: swap.swapAddress,
@@ -322,7 +349,7 @@ message anywhere: **acceptance is funding**.
322
349
  import { httpTransport, requestLightningSend } from "@arkade-os/swap";
323
350
 
324
351
  // invoice facts from YOUR OWN decoder — the module takes facts, not a decoder
325
- const swap = await requestLightningSend(wallet, arkServerUrl, httpTransport(solverUrl), {
352
+ const swap = await requestLightningSend(wallet, httpTransport(solverUrl), {
326
353
  invoice: { raw: bolt11, paymentHash, amountSats, expiresAt },
327
354
  });
328
355
  // quote verified against the LOCAL derivation and gated; now fund and go offline:
@@ -395,7 +422,7 @@ import {
395
422
  swapSecretsToRecord,
396
423
  } from "@arkade-os/swap";
397
424
 
398
- const swap = await requestOnchainSend(wallet, arkServerUrl, httpTransport(solverUrl), {
425
+ const swap = await requestOnchainSend(wallet, httpTransport(solverUrl), {
399
426
  amount: 100_000,
400
427
  amountSide: "to",
401
428
  payoutPubkey,
@@ -759,6 +786,23 @@ through their stored `preimageHex` or their HD descriptor, and `DB_VERSION` is u
759
786
 
760
787
  Notes from before 0.0.1, kept for consumers who tracked the branch.
761
788
 
789
+ - **`refundIfUnresolved` reports an exited lockup, and its input gained `paymentHash`.**
790
+ `RefundOutcome` has a new `{ outcome: "exited"; outpoints; status }` variant: a lockup whose
791
+ outputs were unilaterally exited lives onchain under the VHTLC script, where no offchain refund
792
+ reaches it. It used to come back as `nothing_to_refund`, which reads as "already resolved" over
793
+ money still sitting at the script — the swept case gets `needs_recovery` for the same reason, and
794
+ this is deliberately **not** that variant: recovery into a fresh batch is a spend no batch can
795
+ make for an onchain output. Complete the unroll and spend the outputs onchain instead.
796
+
797
+ Two required changes for direct callers. The input gains **`paymentHash`** (`sha256(P)` hex — the
798
+ quote's `payment_hash`, which callers already hold): the exit is read through `readLockupFate`,
799
+ and the VHTLC script cannot supply it, since its `preimageHash` is a `hash160` of the same secret.
800
+ And the `indexer` parameter widened from `RefundIndexer` to **`LockupSpendIndexer`**, so it must
801
+ now carry `getVirtualTxs` as well as `getVtxos` — a real `RestIndexerProvider` already does.
802
+
803
+ A lockup funded in two sends of which only one exited reports `exited` for the whole thing and
804
+ leaves the live half unrefunded. That matches `RfqSwapManager`, which reports the same lockup
805
+ `exited` on the same any-output rule; the two must not disagree.
762
806
  - **`RfqSwapManager` can own its own persistence.** New optional
763
807
  `RfqSwapManagerDeps.repository`, new `restoreFromRepository()` and `pruneRetiredSwaps()`, and
764
808
  `addSwap(swap, origin?)` gains an optional second argument. Nothing narrows and nothing is
@@ -812,7 +856,7 @@ Notes from before 0.0.1, kept for consumers who tracked the branch.
812
856
  silently overwritten.
813
857
  - **`readLockupFate` names the spends it observed.** `claimed` and `returned` now carry
814
858
  `spends: readonly LockupSpend[]`, one per spent lockup output, with the `checkpointTxid` that
815
- `spentBy` names and the `arkTxid` that rode it. History correlation wants `arkTxid`; the
859
+ `spentBy` names and the `txid` that rode it. History correlation wants `txid`; the
816
860
  checkpoint txid is the wrong value to correlate on alone. `unknown` and `open` claim no spend.
817
861
 
818
862
  - **Every derived address changed again, in both corridors — the unilateral ladder was re-spaced.**
@@ -884,8 +928,8 @@ stored?)`. The returned `ProvisionedKey` / `ProvisionedClaimSecret` replace `Swa
884
928
  quote at `verifyLockupAddress`. Upgrade both sides before expecting fills.
885
929
  - **`cancelOffer` and `restoreAssetSwaps` take an options object.** `cancelOffer(wallet, url,
886
930
  offerHex, { repository, fundingTxid?, swapAddress? })` — the repository is required because the
887
- call now records its own outcome. `restoreAssetSwaps(indexer, txs, existingIds, { serverPubkey,
888
- scanned? })` — the server key is required because a spend is classified by rebuilding the
931
+ call now records its own outcome. `restoreAssetSwaps(indexer, txs, existingIds, { operatorPubkey,
932
+ scanned? })` — the operator key is required because a spend is classified by rebuilding the
889
933
  covenant and matching the leaf it took.
890
934
  - **`isCancelSpend` is gone**, replaced by `classifySpend`, and `Tx.assets` with it. The old test
891
935
  read what a transaction moved, which a wallet reports as a _net_ delta: once the deposit is a
@@ -927,7 +971,7 @@ scanned? })` — the server key is required because a spend is classified by reb
927
971
  kind: "lightning_receive",
928
972
  lockupAddress: result.address,
929
973
  profile: {
930
- ...rfqSecretsProfile(result.secrets, result.treeParams.paymentHash),
974
+ ...rfqSecretsProfile(result.secrets, result.contractParams.paymentHash),
931
975
  expectedAmount: result.expectedAmount,
932
976
  payoutAddress: result.payoutAddress,
933
977
  },
@@ -997,7 +1041,7 @@ scanned? })` — the server key is required because a spend is classified by reb
997
1041
  when `persisted` is true — the callback is documented as following a persisted change, and a
998
1042
  consumer caching from it must not run ahead of the store.
999
1043
  - **`lightningSendProgram` and `htlcSendProgram` are gone** along with the program-artifact layer
1000
- they compiled. Derive scripts through `lightningSendVtxoScript` / `onchainHtlcScript`.
1044
+ they compiled. Derive scripts through `lightningSendContract` / `onchainHtlcScript`.
1001
1045
  - **The receive corridors are wired, and the wire shape settled.** `lightningReceiveRequest` is
1002
1046
  new; `onchainReceiveRequest`'s profile now matches the shipped solver schema (`payment_hash`,
1003
1047
  `claim_packet`, `refund_pubkey`, `payout_address`, `payout_pubkey` — the earlier
@@ -1008,7 +1052,7 @@ scanned? })` — the server key is required because a spend is classified by reb
1008
1052
  corridor's fee — and refuses quotes whose `to_amount` reprices the invoice; solvers charge
1009
1053
  per-corridor fees on all four pairs, and funding the bare invoice amount underfunds by exactly
1010
1054
  the fee.
1011
- - **`lightningSendVtxoScript` takes two new required fields**: `senderPubkey` (the trader's VHTLC
1055
+ - **`lightningSendContract` takes two new required fields**: `senderPubkey` (the trader's VHTLC
1012
1056
  sender key — generate, persist, see `requestLightningSend`) and `receiverPkScript` (the solver's
1013
1057
  claim destination, from `profile.receiver_pk_script`). Callers that built the lockup directly
1014
1058
  must supply both; callers going through `requestLightningSend` are unaffected.
@@ -309,9 +309,8 @@ import { hex as hex3 } from "@scure/base";
309
309
  import { ripemd160 as ripemd1602 } from "@noble/hashes/legacy.js";
310
310
  import {
311
311
  ArkAddress as ArkAddress2,
312
- RestArkProvider,
313
312
  VHTLC,
314
- getNetwork,
313
+ networkFromArkadeInfo,
315
314
  resolveEmulatorPubkey,
316
315
  toXOnly
317
316
  } from "@arkade-os/sdk";
@@ -338,11 +337,14 @@ var ONCHAIN_SEND_PAIR = rfqPair(ARKADE_BTC, ONCHAIN_BTC);
338
337
  var ONCHAIN_RECEIVE_PAIR = rfqPair(ONCHAIN_BTC, ARKADE_BTC);
339
338
  var RFQ_TERMINAL_STATES = ["settled", "refused", "expired", "refunded", "stuck"];
340
339
  var SwapRefusal = class extends Error {
340
+ /** Literal-typed so the v2 error taxonomy's union discriminates on `name`
341
+ * — a `string` here collapses the discriminant for every member. Same value
342
+ * the constructor has always set, moved to a field initializer. */
343
+ name = "SwapRefusal";
341
344
  reason;
342
345
  rfqId;
343
346
  constructor(reason, rfqId) {
344
347
  super(`solver refused: ${reason}`);
345
- this.name = "SwapRefusal";
346
348
  this.reason = reason;
347
349
  this.rfqId = rfqId;
348
350
  }
@@ -422,8 +424,22 @@ var assertPairLength = (pair) => {
422
424
  };
423
425
  var verifyLockupAddress = (quote, derivedAddress) => {
424
426
  const quoted = quote.profile?.lockup_address;
425
- if (derivedAddress !== quoted) throw new AddressMismatch(derivedAddress, quoted);
426
- return derivedAddress;
427
+ const candidates = Array.isArray(derivedAddress) ? derivedAddress : [derivedAddress];
428
+ const matched = candidates.find((address) => address === quoted);
429
+ if (matched === void 0) throw new AddressMismatch(candidates, quoted);
430
+ return matched;
431
+ };
432
+ var LOCKUP_SHAPE_VARIANTS = [void 0, "preTimelockedRefund"];
433
+ var matchQuotedLockup = (quote, hrp, serverPubkey, build) => {
434
+ const candidates = LOCKUP_SHAPE_VARIANTS.map((legacy) => {
435
+ const script = build(legacy);
436
+ return { script, address: script.address(hrp, serverPubkey).encode(), legacy };
437
+ });
438
+ const matchedAddress = verifyLockupAddress(
439
+ quote,
440
+ candidates.map((candidate) => candidate.address)
441
+ );
442
+ return candidates.find((candidate) => candidate.address === matchedAddress);
427
443
  };
428
444
  var assertFundable = (input) => {
429
445
  const fail = (reason, message) => {
@@ -594,22 +610,22 @@ var relayTransport = (relayUrl, options) => {
594
610
  };
595
611
  var SEQUENCE_GRANULARITY_SECONDS = 512;
596
612
  var SOLO_REFUND_HEADROOM_SECONDS = 8 * SEQUENCE_GRANULARITY_SECONDS;
597
- var unilateralClaimDelay = (serverExitDelaySeconds) => {
598
- if (!Number.isFinite(serverExitDelaySeconds) || serverExitDelaySeconds < SEQUENCE_GRANULARITY_SECONDS) {
613
+ var unilateralClaimDelay = (operatorExitDelaySeconds) => {
614
+ if (!Number.isFinite(operatorExitDelaySeconds) || operatorExitDelaySeconds < SEQUENCE_GRANULARITY_SECONDS) {
599
615
  throw new Error(
600
- `server exit delay must be at least ${SEQUENCE_GRANULARITY_SECONDS}s of seconds, got ${serverExitDelaySeconds}`
616
+ `operator exit delay must be at least ${SEQUENCE_GRANULARITY_SECONDS}s of seconds, got ${operatorExitDelaySeconds}`
601
617
  );
602
618
  }
603
- if (serverExitDelaySeconds > 65535 * SEQUENCE_GRANULARITY_SECONDS - SOLO_REFUND_HEADROOM_SECONDS) {
619
+ if (operatorExitDelaySeconds > 65535 * SEQUENCE_GRANULARITY_SECONDS - SOLO_REFUND_HEADROOM_SECONDS) {
604
620
  throw new Error(
605
- `server exit delay ${serverExitDelaySeconds}s exceeds what BIP68 can encode once the solo refund's headroom is stacked above it`
621
+ `operator exit delay ${operatorExitDelaySeconds}s exceeds what BIP68 can encode once the solo refund's headroom is stacked above it`
606
622
  );
607
623
  }
608
- return Math.ceil(serverExitDelaySeconds / SEQUENCE_GRANULARITY_SECONDS) * SEQUENCE_GRANULARITY_SECONDS;
624
+ return Math.ceil(operatorExitDelaySeconds / SEQUENCE_GRANULARITY_SECONDS) * SEQUENCE_GRANULARITY_SECONDS;
609
625
  };
610
626
  var unilateralRefundDelay = (claimDelay) => claimDelay;
611
627
  var unilateralRefundWithoutReceiverDelay = (claimDelay) => claimDelay + SOLO_REFUND_HEADROOM_SECONDS;
612
- function lightningSendVtxoScript(params) {
628
+ function lightningSendContract(params) {
613
629
  const seconds = (value) => ({
614
630
  type: "seconds",
615
631
  value: BigInt(value)
@@ -617,7 +633,7 @@ function lightningSendVtxoScript(params) {
617
633
  return new VHTLC.ScriptV2({
618
634
  sender: params.senderPubkey,
619
635
  receiver: params.solverPubkey,
620
- server: params.serverPubkey,
636
+ server: params.operatorPubkey,
621
637
  preimageHash: ripemd1602(hex3.decode(params.paymentHash)),
622
638
  refundLocktime: BigInt(params.refundLocktime),
623
639
  unilateralClaimDelay: seconds(params.claimDelay),
@@ -625,22 +641,20 @@ function lightningSendVtxoScript(params) {
625
641
  unilateralRefundWithoutReceiverDelay: seconds(
626
642
  unilateralRefundWithoutReceiverDelay(params.claimDelay)
627
643
  ),
628
- nonInteractiveClaim: {
644
+ nonInteractiveParameters: {
629
645
  receiverPkScript: params.receiverPkScript,
630
- emulatorPubkey: params.emulatorPubkey
631
- },
632
- nonInteractiveRefund: {
633
646
  senderPkScript: params.refundPkScript,
634
- emulatorPubkey: params.emulatorPubkey
647
+ emulatorPubkey: params.emulatorPubkey,
648
+ ...params.legacy !== void 0 && { legacy: params.legacy }
635
649
  }
636
650
  });
637
651
  }
638
- async function requestLightningSend(wallet, arkServerUrl, transport, params) {
652
+ async function requestLightningSend(wallet, transport, params) {
639
653
  const rfqId = params.rfqId ?? newRfqId();
640
654
  const secrets = await provisionRefundKey(wallet);
641
655
  const senderPubkey = secrets.pubkey;
642
656
  const refundAddress = secrets.address;
643
- const info = await new RestArkProvider(arkServerUrl).getInfo();
657
+ const info = await wallet.getArkadeInfo({ requireLive: true });
644
658
  const quote = await transport.requestQuote(
645
659
  lightningSendRequest({ rfqId, invoice: params.invoice.raw, refundAddress, senderPubkey })
646
660
  );
@@ -661,12 +675,12 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
661
675
  `quote from_amount ${quote.from_amount} is below the invoice amount \u2014 a negative spread is not a quote`
662
676
  );
663
677
  }
664
- const serverPubkey = toXOnly(hex3.decode(info.signerPubkey), "ark signer key");
665
- const network = getNetwork(info.network);
666
- const treeParams = {
678
+ const operatorPubkey = toXOnly(hex3.decode(info.signerPubkey), "ark signer key");
679
+ const network = networkFromArkadeInfo(info);
680
+ const contractParams = {
667
681
  solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
668
682
  refundLocktime: quote.refund_locktime,
669
- serverPubkey,
683
+ operatorPubkey,
670
684
  paymentHash: params.invoice.paymentHash,
671
685
  claimDelay: unilateralClaimDelay(Number(info.unilateralExitDelay)),
672
686
  emulatorPubkey: toXOnly(
@@ -677,9 +691,18 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
677
691
  receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
678
692
  refundPkScript: secrets.pkScript
679
693
  };
680
- const script = lightningSendVtxoScript(treeParams);
681
- const address = script.address(network.hrp, serverPubkey).encode();
682
- verifyLockupAddress(quote, address);
694
+ const matched = matchQuotedLockup(
695
+ quote,
696
+ network.hrp,
697
+ operatorPubkey,
698
+ (legacy) => lightningSendContract({ ...contractParams, ...legacy !== void 0 && { legacy } })
699
+ );
700
+ const script = matched.script;
701
+ const address = matched.address;
702
+ const matchedContractParams = {
703
+ ...contractParams,
704
+ ...matched.legacy !== void 0 && { legacy: matched.legacy }
705
+ };
683
706
  assertFundable({
684
707
  quote,
685
708
  invoiceExpiresAt: params.invoice.expiresAt,
@@ -698,7 +721,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
698
721
  refundAddress,
699
722
  senderPubkey,
700
723
  secrets,
701
- treeParams
724
+ contractParams: matchedContractParams
702
725
  };
703
726
  }
704
727
  var offerTermsFromQuote = (quote, assets) => {
@@ -763,19 +786,23 @@ function deriveOnchainSend(input) {
763
786
  if (refundLocktime === void 0 || htlcPubkey === void 0 || htlcLocktime === void 0 || minConfirmations === void 0 || receiverPkScriptHex === void 0) {
764
787
  throw new Error("onchain-send quote is missing a binding field");
765
788
  }
766
- const script = lightningSendVtxoScript({
789
+ const contractParams = {
767
790
  solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
768
791
  refundLocktime,
769
- serverPubkey: input.serverPubkey,
792
+ operatorPubkey: input.operatorPubkey,
770
793
  paymentHash: input.paymentHash,
771
794
  claimDelay: input.claimDelay,
772
795
  emulatorPubkey: input.emulatorPubkey,
773
796
  senderPubkey: input.senderPubkey,
774
797
  receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
775
798
  refundPkScript: ArkAddress2.decode(input.refundAddress).pkScript
776
- });
777
- const address = script.address(input.hrp, input.serverPubkey).encode();
778
- verifyLockupAddress(quote, address);
799
+ };
800
+ const { script, address } = matchQuotedLockup(
801
+ quote,
802
+ input.hrp,
803
+ input.operatorPubkey,
804
+ (legacy) => lightningSendContract({ ...contractParams, ...legacy !== void 0 && { legacy } })
805
+ );
779
806
  const htlcParams = {
780
807
  paymentHash: input.paymentHash,
781
808
  claimKey: input.payoutPubkey,
@@ -796,7 +823,7 @@ function deriveOnchainSend(input) {
796
823
  minConfirmations
797
824
  };
798
825
  }
799
- async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
826
+ async function requestOnchainSend(wallet, transport, params) {
800
827
  const rfqId = params.rfqId ?? newRfqId();
801
828
  const secrets = await provisionClaimSecret(wallet, { preimage: params.preimage });
802
829
  if (secrets.mustPersistPreimage) {
@@ -807,7 +834,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
807
834
  const paymentHash = hex3.encode(secrets.paymentHash);
808
835
  const senderPubkey = secrets.pubkey;
809
836
  const [info, refundAddress] = await Promise.all([
810
- new RestArkProvider(arkServerUrl).getInfo(),
837
+ wallet.getArkadeInfo({ requireLive: true }),
811
838
  wallet.getAddress()
812
839
  ]);
813
840
  const quote = await transport.requestQuote(
@@ -821,12 +848,12 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
821
848
  amountSide: params.amountSide
822
849
  })
823
850
  );
824
- const network = getNetwork(info.network);
851
+ const network = networkFromArkadeInfo(info);
825
852
  const derived = deriveOnchainSend({
826
853
  quote,
827
854
  paymentHash,
828
855
  payoutPubkey: params.payoutPubkey,
829
- serverPubkey: toXOnly(hex3.decode(info.signerPubkey), "ark signer key"),
856
+ operatorPubkey: toXOnly(hex3.decode(info.signerPubkey), "ark signer key"),
830
857
  emulatorPubkey: toXOnly(
831
858
  hex3.decode(resolveEmulatorPubkey(network, params.emulatorPubkey)),
832
859
  "emulator signer key"
@@ -863,6 +890,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
863
890
  htlcParams: derived.htlcParams,
864
891
  l1Network: derived.l1Network,
865
892
  minConfirmations: derived.minConfirmations,
893
+ refundLocktime: derived.refundLocktime,
866
894
  senderPubkey,
867
895
  secrets
868
896
  };
@@ -934,7 +962,7 @@ var assertReceivable = (input) => {
934
962
  );
935
963
  }
936
964
  };
937
- function receiveVtxoScript(params) {
965
+ function lightningReceiveContract(params) {
938
966
  const seconds = (value) => ({
939
967
  type: "seconds",
940
968
  value: BigInt(value)
@@ -942,7 +970,7 @@ function receiveVtxoScript(params) {
942
970
  return new VHTLC.ScriptV2({
943
971
  sender: params.solverPubkey,
944
972
  receiver: params.payoutPubkey,
945
- server: params.serverPubkey,
973
+ server: params.operatorPubkey,
946
974
  preimageHash: ripemd1602(hex3.decode(params.paymentHash)),
947
975
  refundLocktime: BigInt(params.refundLocktime),
948
976
  unilateralClaimDelay: seconds(params.claimDelay),
@@ -950,13 +978,11 @@ function receiveVtxoScript(params) {
950
978
  unilateralRefundWithoutReceiverDelay: seconds(
951
979
  unilateralRefundWithoutReceiverDelay(params.claimDelay)
952
980
  ),
953
- nonInteractiveClaim: {
981
+ nonInteractiveParameters: {
954
982
  receiverPkScript: params.payoutPkScript,
955
- emulatorPubkey: params.emulatorPubkey
956
- },
957
- nonInteractiveRefund: {
958
983
  senderPkScript: params.solverRefundPkScript,
959
- emulatorPubkey: params.emulatorPubkey
984
+ emulatorPubkey: params.emulatorPubkey,
985
+ ...params.legacy !== void 0 && { legacy: params.legacy }
960
986
  }
961
987
  });
962
988
  }
@@ -969,10 +995,10 @@ function deriveLightningReceive(input) {
969
995
  if (refundLocktime === void 0 || invoice === void 0 || solverRefundPkScriptHex === void 0) {
970
996
  throw new Error("lightning-receive quote is missing a binding field");
971
997
  }
972
- const treeParams = {
998
+ const contractParams = {
973
999
  solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
974
1000
  refundLocktime,
975
- serverPubkey: input.serverPubkey,
1001
+ operatorPubkey: input.operatorPubkey,
976
1002
  paymentHash: input.paymentHash,
977
1003
  claimDelay: input.claimDelay,
978
1004
  emulatorPubkey: input.emulatorPubkey,
@@ -980,12 +1006,25 @@ function deriveLightningReceive(input) {
980
1006
  payoutPubkey: input.payoutPubkey,
981
1007
  payoutPkScript: ArkAddress2.decode(input.payoutAddress).pkScript
982
1008
  };
983
- const script = receiveVtxoScript(treeParams);
984
- const address = script.address(input.hrp, input.serverPubkey).encode();
985
- verifyLockupAddress(quote, address);
986
- return { address, swapPkScript: script.pkScript, script, invoice, refundLocktime, treeParams };
1009
+ const matched = matchQuotedLockup(
1010
+ quote,
1011
+ input.hrp,
1012
+ input.operatorPubkey,
1013
+ (legacy) => lightningReceiveContract({ ...contractParams, ...legacy !== void 0 && { legacy } })
1014
+ );
1015
+ return {
1016
+ address: matched.address,
1017
+ swapPkScript: matched.script.pkScript,
1018
+ script: matched.script,
1019
+ invoice,
1020
+ refundLocktime,
1021
+ contractParams: {
1022
+ ...contractParams,
1023
+ ...matched.legacy !== void 0 && { legacy: matched.legacy }
1024
+ }
1025
+ };
987
1026
  }
988
- async function requestLightningReceive(wallet, arkServerUrl, transport, params) {
1027
+ async function requestLightningReceive(wallet, transport, params) {
989
1028
  const rfqId = params.rfqId ?? newRfqId();
990
1029
  const secrets = await provisionClaimSecret(wallet);
991
1030
  if (secrets.mustPersistPreimage) {
@@ -997,7 +1036,7 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
997
1036
  const paymentHash = hex3.encode(secrets.paymentHash);
998
1037
  const payoutPubkey = secrets.pubkey;
999
1038
  const [info, payoutAddress] = await Promise.all([
1000
- new RestArkProvider(arkServerUrl).getInfo(),
1039
+ wallet.getArkadeInfo({ requireLive: true }),
1001
1040
  wallet.getAddress()
1002
1041
  ]);
1003
1042
  const claimPacket = await sealClaimPacket({
@@ -1016,13 +1055,13 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
1016
1055
  })
1017
1056
  );
1018
1057
  assertQuotedAmount(quote, params.amountSide, params.amount);
1019
- const network = getNetwork(info.network);
1058
+ const network = networkFromArkadeInfo(info);
1020
1059
  const derived = deriveLightningReceive({
1021
1060
  quote,
1022
1061
  paymentHash,
1023
1062
  payoutPubkey,
1024
1063
  payoutAddress,
1025
- serverPubkey: toXOnly(hex3.decode(info.signerPubkey), "ark signer key"),
1064
+ operatorPubkey: toXOnly(hex3.decode(info.signerPubkey), "ark signer key"),
1026
1065
  emulatorPubkey: toXOnly(
1027
1066
  hex3.decode(resolveEmulatorPubkey(network, params.emulatorPubkey)),
1028
1067
  "emulator signer key"
@@ -1056,7 +1095,7 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
1056
1095
  payoutAddress,
1057
1096
  payoutPubkey,
1058
1097
  secrets,
1059
- treeParams: derived.treeParams
1098
+ contractParams: derived.contractParams
1060
1099
  };
1061
1100
  }
1062
1101
  function deriveOnchainReceive(input) {
@@ -1071,19 +1110,26 @@ function deriveOnchainReceive(input) {
1071
1110
  if (refundLocktime === void 0 || claimPubkey === void 0 || htlcLocktime === void 0 || minConfirmations === void 0 || solverRefundPkScriptHex === void 0) {
1072
1111
  throw new Error("onchain-receive quote is missing a binding field");
1073
1112
  }
1074
- const script = receiveVtxoScript({
1113
+ const contractParams = {
1075
1114
  solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
1076
1115
  refundLocktime,
1077
- serverPubkey: input.serverPubkey,
1116
+ operatorPubkey: input.operatorPubkey,
1078
1117
  paymentHash: input.paymentHash,
1079
1118
  claimDelay: input.claimDelay,
1080
1119
  emulatorPubkey: input.emulatorPubkey,
1081
1120
  solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
1082
1121
  payoutPubkey: input.payoutPubkey,
1083
1122
  payoutPkScript: ArkAddress2.decode(input.payoutAddress).pkScript
1084
- });
1085
- const address = script.address(input.hrp, input.serverPubkey).encode();
1086
- verifyLockupAddress(quote, address);
1123
+ };
1124
+ const { script, address } = matchQuotedLockup(
1125
+ quote,
1126
+ input.hrp,
1127
+ input.operatorPubkey,
1128
+ (legacy) => lightningReceiveContract({
1129
+ ...contractParams,
1130
+ ...legacy !== void 0 && { legacy }
1131
+ })
1132
+ );
1087
1133
  const htlc = onchainHtlcScript(
1088
1134
  {
1089
1135
  paymentHash: input.paymentHash,
@@ -1104,7 +1150,7 @@ function deriveOnchainReceive(input) {
1104
1150
  minConfirmations
1105
1151
  };
1106
1152
  }
1107
- async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
1153
+ async function requestOnchainReceive(wallet, transport, params) {
1108
1154
  const rfqId = params.rfqId ?? newRfqId();
1109
1155
  const secrets = await provisionClaimSecret(wallet);
1110
1156
  if (secrets.mustPersistPreimage) {
@@ -1116,7 +1162,7 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
1116
1162
  const paymentHash = hex3.encode(secrets.paymentHash);
1117
1163
  const payoutPubkey = secrets.pubkey;
1118
1164
  const [info, payoutAddress] = await Promise.all([
1119
- new RestArkProvider(arkServerUrl).getInfo(),
1165
+ wallet.getArkadeInfo({ requireLive: true }),
1120
1166
  wallet.getAddress()
1121
1167
  ]);
1122
1168
  const claimPacket = await sealClaimPacket({
@@ -1136,14 +1182,14 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
1136
1182
  })
1137
1183
  );
1138
1184
  assertQuotedAmount(quote, params.amountSide, params.amount);
1139
- const network = getNetwork(info.network);
1185
+ const network = networkFromArkadeInfo(info);
1140
1186
  const derived = deriveOnchainReceive({
1141
1187
  quote,
1142
1188
  paymentHash,
1143
1189
  payoutPubkey,
1144
1190
  payoutAddress,
1145
1191
  refundPubkey: params.refundPubkey,
1146
- serverPubkey: toXOnly(hex3.decode(info.signerPubkey), "ark signer key"),
1192
+ operatorPubkey: toXOnly(hex3.decode(info.signerPubkey), "ark signer key"),
1147
1193
  emulatorPubkey: toXOnly(
1148
1194
  hex3.decode(resolveEmulatorPubkey(network, params.emulatorPubkey)),
1149
1195
  "emulator signer key"
@@ -1232,7 +1278,7 @@ export {
1232
1278
  unilateralClaimDelay,
1233
1279
  unilateralRefundDelay,
1234
1280
  unilateralRefundWithoutReceiverDelay,
1235
- lightningSendVtxoScript,
1281
+ lightningSendContract,
1236
1282
  requestLightningSend,
1237
1283
  offerTermsFromQuote,
1238
1284
  onchainSendRequest,
@@ -1243,7 +1289,7 @@ export {
1243
1289
  MIN_CLAIM_WINDOW_SECONDS,
1244
1290
  verifyReceiveInvoice,
1245
1291
  assertReceivable,
1246
- receiveVtxoScript,
1292
+ lightningReceiveContract,
1247
1293
  deriveLightningReceive,
1248
1294
  requestLightningReceive,
1249
1295
  deriveOnchainReceive,