@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 +59 -15
- package/dist/{chunk-5NPYNQ5V.js → chunk-AM3NNUMR.js} +112 -66
- package/dist/index.cjs +763 -227
- package/dist/index.d.cts +184 -47
- package/dist/index.d.ts +184 -47
- package/dist/index.js +653 -169
- package/dist/nostr.cjs +4 -1
- 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 +4 -4
- package/dist/repositories/realm/index.d.ts +4 -4
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/{repository-C8FXlmHY.d.cts → repository-BWP1UstE.d.cts} +124 -59
- package/dist/{repository-Cw2EvvZG.d.ts → repository-DLpH41ZO.d.ts} +124 -59
- package/dist/{rfq-DglvHMNC.d.ts → rfq-CzCGjICq.d.cts} +86 -48
- package/dist/{rfq-DglvHMNC.d.cts → rfq-CzCGjICq.d.ts} +86 -48
- package/package.json +4 -3
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 (`
|
|
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
|
|
132
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 `
|
|
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, {
|
|
888
|
-
scanned? })` — 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.
|
|
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 `
|
|
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
|
-
- **`
|
|
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
|
-
|
|
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
|
-
|
|
426
|
-
|
|
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 = (
|
|
598
|
-
if (!Number.isFinite(
|
|
613
|
+
var unilateralClaimDelay = (operatorExitDelaySeconds) => {
|
|
614
|
+
if (!Number.isFinite(operatorExitDelaySeconds) || operatorExitDelaySeconds < SEQUENCE_GRANULARITY_SECONDS) {
|
|
599
615
|
throw new Error(
|
|
600
|
-
`
|
|
616
|
+
`operator exit delay must be at least ${SEQUENCE_GRANULARITY_SECONDS}s of seconds, got ${operatorExitDelaySeconds}`
|
|
601
617
|
);
|
|
602
618
|
}
|
|
603
|
-
if (
|
|
619
|
+
if (operatorExitDelaySeconds > 65535 * SEQUENCE_GRANULARITY_SECONDS - SOLO_REFUND_HEADROOM_SECONDS) {
|
|
604
620
|
throw new Error(
|
|
605
|
-
`
|
|
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(
|
|
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
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
665
|
-
const network =
|
|
666
|
-
const
|
|
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
|
-
|
|
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
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
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
|
|
789
|
+
const contractParams = {
|
|
767
790
|
solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
|
|
768
791
|
refundLocktime,
|
|
769
|
-
|
|
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 =
|
|
778
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
851
|
+
const network = networkFromArkadeInfo(info);
|
|
825
852
|
const derived = deriveOnchainSend({
|
|
826
853
|
quote,
|
|
827
854
|
paymentHash,
|
|
828
855
|
payoutPubkey: params.payoutPubkey,
|
|
829
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
998
|
+
const contractParams = {
|
|
973
999
|
solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
|
|
974
1000
|
refundLocktime,
|
|
975
|
-
|
|
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
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
1058
|
+
const network = networkFromArkadeInfo(info);
|
|
1020
1059
|
const derived = deriveLightningReceive({
|
|
1021
1060
|
quote,
|
|
1022
1061
|
paymentHash,
|
|
1023
1062
|
payoutPubkey,
|
|
1024
1063
|
payoutAddress,
|
|
1025
|
-
|
|
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
|
-
|
|
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
|
|
1113
|
+
const contractParams = {
|
|
1075
1114
|
solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
|
|
1076
1115
|
refundLocktime,
|
|
1077
|
-
|
|
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 =
|
|
1086
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1292
|
+
lightningReceiveContract,
|
|
1247
1293
|
deriveLightningReceive,
|
|
1248
1294
|
requestLightningReceive,
|
|
1249
1295
|
deriveOnchainReceive,
|