@satora/swap 1.7.1 → 1.8.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/dist/index.cjs +82 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -76
- package/dist/index.d.ts +102 -76
- package/dist/index.js +82 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -396,6 +396,81 @@ interface ContractManager {
|
|
|
396
396
|
dispose(): void;
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
/**
|
|
400
|
+
* A standalone subscriber to the server's swap-status WebSocket (`GET /ws`).
|
|
401
|
+
*
|
|
402
|
+
* Holds a single reconnecting connection, tracks a live set of subscribed swap
|
|
403
|
+
* ids, and emits `{ swapId, status }` on every status transition the server
|
|
404
|
+
* pushes — including the snapshot the server sends right after a subscribe. This
|
|
405
|
+
* is a *hint* feed only: it neither trusts nor acts on the status. A consumer is
|
|
406
|
+
* expected to verify against the chain (the contract managers) before doing
|
|
407
|
+
* anything with it.
|
|
408
|
+
*
|
|
409
|
+
* Protocol (see `swap/src/api/ws.rs`):
|
|
410
|
+
* → {"op":"subscribe","channel":"swap_status","args":[<uuid>,...]} (≤64 ids/frame)
|
|
411
|
+
* ← {"channel":"swap_status","data":{"<uuid>":"<status>",...}}
|
|
412
|
+
* ← {"op":"subscribed",...} (ack) ← {"op":...,"message":...} (error)
|
|
413
|
+
* A single connection carries up to 256 subscriptions; that cap is enforced
|
|
414
|
+
* client-side (see {@link WsStatusSource.subscribe}) because the server silently
|
|
415
|
+
* drops the overflow.
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
/** A status update pushed by the server for one swap. */
|
|
419
|
+
type SwapStatusUpdate = {
|
|
420
|
+
swapId: string;
|
|
421
|
+
status: SwapStatus;
|
|
422
|
+
};
|
|
423
|
+
/** The minimal WebSocket surface the source needs (the global `WebSocket` satisfies it). */
|
|
424
|
+
interface WebSocketLike {
|
|
425
|
+
send(data: string): void;
|
|
426
|
+
close(): void;
|
|
427
|
+
onopen: (() => void) | null;
|
|
428
|
+
onmessage: ((event: {
|
|
429
|
+
data: unknown;
|
|
430
|
+
}) => void) | null;
|
|
431
|
+
onclose: (() => void) | null;
|
|
432
|
+
onerror: ((event: unknown) => void) | null;
|
|
433
|
+
}
|
|
434
|
+
type WsStatusSourceOptions = {
|
|
435
|
+
/** Server base URL (http/https); the `ws(s)://…/ws` endpoint is derived from it. */
|
|
436
|
+
serverUrl: string;
|
|
437
|
+
/** WebSocket factory — defaults to the global `WebSocket` (browsers, Node ≥ 22). */
|
|
438
|
+
webSocketFactory?: (url: string) => WebSocketLike;
|
|
439
|
+
/** Reconnect backoff floor/ceiling (ms). Defaults: 1_000 / 30_000. */
|
|
440
|
+
reconnectMinMs?: number;
|
|
441
|
+
reconnectMaxMs?: number;
|
|
442
|
+
};
|
|
443
|
+
declare class WsStatusSource {
|
|
444
|
+
#private;
|
|
445
|
+
constructor(options: WsStatusSourceOptions);
|
|
446
|
+
/** Open the connection and begin (re)subscribing. Idempotent. */
|
|
447
|
+
start(): void;
|
|
448
|
+
/** Close the connection and stop reconnecting. */
|
|
449
|
+
stop(): void;
|
|
450
|
+
/** Emit for each pushed status update. Returns an unsubscribe fn. */
|
|
451
|
+
onStatus(cb: (update: SwapStatusUpdate) => void): () => void;
|
|
452
|
+
/**
|
|
453
|
+
* Emit whenever the socket comes (back) up after a disruption — a drop, or a
|
|
454
|
+
* first connect that had to be retried. NOT emitted on a pristine first open:
|
|
455
|
+
* hints were never live before it, so there is no gap to recover from. While
|
|
456
|
+
* the socket was down, pushed transitions were lost for good (the server only
|
|
457
|
+
* snapshots on subscribe), so a consumer should re-verify its world against
|
|
458
|
+
* the chain on this signal.
|
|
459
|
+
*/
|
|
460
|
+
onReconnect(cb: () => void): () => void;
|
|
461
|
+
/**
|
|
462
|
+
* Track these swap ids — subscribe on the socket now if open, else on next open.
|
|
463
|
+
*
|
|
464
|
+
* Enforces the server's per-connection cap locally. The server silently stops
|
|
465
|
+
* adding once it is full (it breaks out of the loop but still acks every id),
|
|
466
|
+
* so sending more would leave us believing ids are subscribed that will never
|
|
467
|
+
* receive an update. Overflow is queued instead and promoted as slots free.
|
|
468
|
+
*/
|
|
469
|
+
subscribe(swapIds: string[]): void;
|
|
470
|
+
/** Stop tracking these swap ids, promoting queued ones into the freed slots. */
|
|
471
|
+
unsubscribe(swapIds: string[]): void;
|
|
472
|
+
}
|
|
473
|
+
|
|
399
474
|
/**
|
|
400
475
|
* The reactive orchestration.
|
|
401
476
|
*
|
|
@@ -533,6 +608,8 @@ type TrackingConfig = {
|
|
|
533
608
|
* remain the source of truth.
|
|
534
609
|
*/
|
|
535
610
|
serverUrl?: string;
|
|
611
|
+
/** WebSocket factory for the hint feed; defaults to the global `WebSocket` (testing). */
|
|
612
|
+
webSocketFactory?: (url: string) => WebSocketLike;
|
|
536
613
|
/** Explicit per-ledger managers, bypassing auto-construction (advanced/testing). */
|
|
537
614
|
managers?: Map<Ledger, ContractManager>;
|
|
538
615
|
/** Ark server URL override; defaults to the mainnet server when unset. */
|
|
@@ -675,6 +752,7 @@ declare class Client {
|
|
|
675
752
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
676
753
|
claim(...args: Parameters<Client$1["claim"]>): ReturnType<Client$1["claim"]>;
|
|
677
754
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
755
|
+
canSponsorClaim(...args: Parameters<Client$1["canSponsorClaim"]>): boolean;
|
|
678
756
|
claimEvmWithSigner(...args: Parameters<Client$1["claimEvmWithSigner"]>): ReturnType<Client$1["claimEvmWithSigner"]>;
|
|
679
757
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
680
758
|
claimViaGasless(...args: Parameters<Client$1["claimViaGasless"]>): ReturnType<Client$1["claimViaGasless"]>;
|
|
@@ -689,6 +767,16 @@ declare class Client {
|
|
|
689
767
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
690
768
|
buildCollabRefundEvmTypedData(...args: Parameters<Client$1["buildCollabRefundEvmTypedData"]>): ReturnType<Client$1["buildCollabRefundEvmTypedData"]>;
|
|
691
769
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
770
|
+
getCollabRefundEvmSignature(...args: Parameters<Client$1["getCollabRefundEvmSignature"]>): ReturnType<Client$1["getCollabRefundEvmSignature"]>;
|
|
771
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
772
|
+
getRehashEvmParams(...args: Parameters<Client$1["getRehashEvmParams"]>): ReturnType<Client$1["getRehashEvmParams"]>;
|
|
773
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
774
|
+
signRehashEvm(...args: Parameters<Client$1["signRehashEvm"]>): ReturnType<Client$1["signRehashEvm"]>;
|
|
775
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
776
|
+
retrySwapWithInvoice(...args: Parameters<Client$1["retrySwapWithInvoice"]>): ReturnType<Client$1["retrySwapWithInvoice"]>;
|
|
777
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
778
|
+
retrySwapWithInvoiceWithSigner(...args: Parameters<Client$1["retrySwapWithInvoiceWithSigner"]>): ReturnType<Client$1["retrySwapWithInvoiceWithSigner"]>;
|
|
779
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
692
780
|
collabRefundEvmSwap(...args: Parameters<Client$1["collabRefundEvmSwap"]>): ReturnType<Client$1["collabRefundEvmSwap"]>;
|
|
693
781
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
694
782
|
submitCollabRefundEvm(...args: Parameters<Client$1["submitCollabRefundEvm"]>): ReturnType<Client$1["submitCollabRefundEvm"]>;
|
|
@@ -718,7 +806,10 @@ declare class Client {
|
|
|
718
806
|
getCoordinatorFundingCallDataPermit2(...args: Parameters<Client$1["getCoordinatorFundingCallDataPermit2"]>): ReturnType<Client$1["getCoordinatorFundingCallDataPermit2"]>;
|
|
719
807
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
720
808
|
getPermit2FundingParamsUnsigned(...args: Parameters<Client$1["getPermit2FundingParamsUnsigned"]>): ReturnType<Client$1["getPermit2FundingParamsUnsigned"]>;
|
|
721
|
-
/**
|
|
809
|
+
/**
|
|
810
|
+
* Fund the swap, then re-verify it against the chain so the lock shows up
|
|
811
|
+
* in the derived actions without waiting on a server hint.
|
|
812
|
+
*/
|
|
722
813
|
fundSwap(...args: Parameters<Client$1["fundSwap"]>): ReturnType<Client$1["fundSwap"]>;
|
|
723
814
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
724
815
|
fundSwapFromSolana(...args: Parameters<Client$1["fundSwapFromSolana"]>): ReturnType<Client$1["fundSwapFromSolana"]>;
|
|
@@ -733,6 +824,8 @@ declare class Client {
|
|
|
733
824
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
734
825
|
fundSwapGasless(...args: Parameters<Client$1["fundSwapGasless"]>): ReturnType<Client$1["fundSwapGasless"]>;
|
|
735
826
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
827
|
+
getEvmFundingExpectation(...args: Parameters<Client$1["getEvmFundingExpectation"]>): ReturnType<Client$1["getEvmFundingExpectation"]>;
|
|
828
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
736
829
|
getSwapDepositorKey(...args: Parameters<Client$1["getSwapDepositorKey"]>): ReturnType<Client$1["getSwapDepositorKey"]>;
|
|
737
830
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
738
831
|
getEvmDepositorKey(...args: Parameters<Client$1["getEvmDepositorKey"]>): ReturnType<Client$1["getEvmDepositorKey"]>;
|
|
@@ -1538,7 +1631,7 @@ type SwapWorkerOptions = {
|
|
|
1538
1631
|
* Resolve once submitted. OMIT for observe-only mode: hints still trigger
|
|
1539
1632
|
* chain re-verifies (fast, cheap derivations), but nothing is ever run.
|
|
1540
1633
|
*/
|
|
1541
|
-
execute?: (swapId: string, actionId: SwapActionId) => Promise<void>;
|
|
1634
|
+
execute?: (swapId: string, actionId: SwapActionId, actions: SwapActions) => Promise<void>;
|
|
1542
1635
|
/**
|
|
1543
1636
|
* Surface an action that needs the user rather than being auto-run — a manual
|
|
1544
1637
|
* `fund`, a refund the user must confirm, or a claim whose retries are spent.
|
|
@@ -1553,83 +1646,16 @@ declare class SwapWorker {
|
|
|
1553
1646
|
constructor(options: SwapWorkerOptions);
|
|
1554
1647
|
/** Watch tracked swaps on the hint feed, wire hints → verify, react to actions. */
|
|
1555
1648
|
start(): void;
|
|
1556
|
-
/** Stop reacting, cancel pending retries, and close the hint feed. */
|
|
1557
|
-
stop(): void;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
/**
|
|
1561
|
-
* A standalone subscriber to the server's swap-status WebSocket (`GET /ws`).
|
|
1562
|
-
*
|
|
1563
|
-
* Holds a single reconnecting connection, tracks a live set of subscribed swap
|
|
1564
|
-
* ids, and emits `{ swapId, status }` on every status transition the server
|
|
1565
|
-
* pushes — including the snapshot the server sends right after a subscribe. This
|
|
1566
|
-
* is a *hint* feed only: it neither trusts nor acts on the status. A consumer is
|
|
1567
|
-
* expected to verify against the chain (the contract managers) before doing
|
|
1568
|
-
* anything with it.
|
|
1569
|
-
*
|
|
1570
|
-
* Protocol (see `swap/src/api/ws.rs`):
|
|
1571
|
-
* → {"op":"subscribe","channel":"swap_status","args":[<uuid>,...]} (≤64 ids/frame)
|
|
1572
|
-
* ← {"channel":"swap_status","data":{"<uuid>":"<status>",...}}
|
|
1573
|
-
* ← {"op":"subscribed",...} (ack) ← {"op":...,"message":...} (error)
|
|
1574
|
-
* A single connection carries up to 256 subscriptions; that cap is enforced
|
|
1575
|
-
* client-side (see {@link WsStatusSource.subscribe}) because the server silently
|
|
1576
|
-
* drops the overflow.
|
|
1577
|
-
*/
|
|
1578
|
-
|
|
1579
|
-
/** A status update pushed by the server for one swap. */
|
|
1580
|
-
type SwapStatusUpdate = {
|
|
1581
|
-
swapId: string;
|
|
1582
|
-
status: SwapStatus;
|
|
1583
|
-
};
|
|
1584
|
-
/** The minimal WebSocket surface the source needs (the global `WebSocket` satisfies it). */
|
|
1585
|
-
interface WebSocketLike {
|
|
1586
|
-
send(data: string): void;
|
|
1587
|
-
close(): void;
|
|
1588
|
-
onopen: (() => void) | null;
|
|
1589
|
-
onmessage: ((event: {
|
|
1590
|
-
data: unknown;
|
|
1591
|
-
}) => void) | null;
|
|
1592
|
-
onclose: (() => void) | null;
|
|
1593
|
-
onerror: ((event: unknown) => void) | null;
|
|
1594
|
-
}
|
|
1595
|
-
type WsStatusSourceOptions = {
|
|
1596
|
-
/** Server base URL (http/https); the `ws(s)://…/ws` endpoint is derived from it. */
|
|
1597
|
-
serverUrl: string;
|
|
1598
|
-
/** WebSocket factory — defaults to the global `WebSocket` (browsers, Node ≥ 22). */
|
|
1599
|
-
webSocketFactory?: (url: string) => WebSocketLike;
|
|
1600
|
-
/** Reconnect backoff floor/ceiling (ms). Defaults: 1_000 / 30_000. */
|
|
1601
|
-
reconnectMinMs?: number;
|
|
1602
|
-
reconnectMaxMs?: number;
|
|
1603
|
-
};
|
|
1604
|
-
declare class WsStatusSource {
|
|
1605
|
-
#private;
|
|
1606
|
-
constructor(options: WsStatusSourceOptions);
|
|
1607
|
-
/** Open the connection and begin (re)subscribing. Idempotent. */
|
|
1608
|
-
start(): void;
|
|
1609
|
-
/** Close the connection and stop reconnecting. */
|
|
1610
|
-
stop(): void;
|
|
1611
|
-
/** Emit for each pushed status update. Returns an unsubscribe fn. */
|
|
1612
|
-
onStatus(cb: (update: SwapStatusUpdate) => void): () => void;
|
|
1613
|
-
/**
|
|
1614
|
-
* Emit whenever the socket comes (back) up after a disruption — a drop, or a
|
|
1615
|
-
* first connect that had to be retried. NOT emitted on a pristine first open:
|
|
1616
|
-
* hints were never live before it, so there is no gap to recover from. While
|
|
1617
|
-
* the socket was down, pushed transitions were lost for good (the server only
|
|
1618
|
-
* snapshots on subscribe), so a consumer should re-verify its world against
|
|
1619
|
-
* the chain on this signal.
|
|
1620
|
-
*/
|
|
1621
|
-
onReconnect(cb: () => void): () => void;
|
|
1622
1649
|
/**
|
|
1623
|
-
*
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
* receive an update. Overflow is queued instead and promoted as slots free.
|
|
1650
|
+
* Put swaps on the hint feed as soon as they are tracked. {@link #onActions}
|
|
1651
|
+
* subscribes a swap when its first action is emitted, but a swap whose leg
|
|
1652
|
+
* cannot be classified yet (an unfunded lock on a chain without `eth_getLogs`)
|
|
1653
|
+
* emits nothing, and without this it would never receive the server hint
|
|
1654
|
+
* that its funding landed.
|
|
1629
1655
|
*/
|
|
1630
1656
|
subscribe(swapIds: string[]): void;
|
|
1631
|
-
/** Stop
|
|
1632
|
-
|
|
1657
|
+
/** Stop reacting, cancel pending retries, and close the hint feed. */
|
|
1658
|
+
stop(): void;
|
|
1633
1659
|
}
|
|
1634
1660
|
|
|
1635
1661
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -396,6 +396,81 @@ interface ContractManager {
|
|
|
396
396
|
dispose(): void;
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
/**
|
|
400
|
+
* A standalone subscriber to the server's swap-status WebSocket (`GET /ws`).
|
|
401
|
+
*
|
|
402
|
+
* Holds a single reconnecting connection, tracks a live set of subscribed swap
|
|
403
|
+
* ids, and emits `{ swapId, status }` on every status transition the server
|
|
404
|
+
* pushes — including the snapshot the server sends right after a subscribe. This
|
|
405
|
+
* is a *hint* feed only: it neither trusts nor acts on the status. A consumer is
|
|
406
|
+
* expected to verify against the chain (the contract managers) before doing
|
|
407
|
+
* anything with it.
|
|
408
|
+
*
|
|
409
|
+
* Protocol (see `swap/src/api/ws.rs`):
|
|
410
|
+
* → {"op":"subscribe","channel":"swap_status","args":[<uuid>,...]} (≤64 ids/frame)
|
|
411
|
+
* ← {"channel":"swap_status","data":{"<uuid>":"<status>",...}}
|
|
412
|
+
* ← {"op":"subscribed",...} (ack) ← {"op":...,"message":...} (error)
|
|
413
|
+
* A single connection carries up to 256 subscriptions; that cap is enforced
|
|
414
|
+
* client-side (see {@link WsStatusSource.subscribe}) because the server silently
|
|
415
|
+
* drops the overflow.
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
/** A status update pushed by the server for one swap. */
|
|
419
|
+
type SwapStatusUpdate = {
|
|
420
|
+
swapId: string;
|
|
421
|
+
status: SwapStatus;
|
|
422
|
+
};
|
|
423
|
+
/** The minimal WebSocket surface the source needs (the global `WebSocket` satisfies it). */
|
|
424
|
+
interface WebSocketLike {
|
|
425
|
+
send(data: string): void;
|
|
426
|
+
close(): void;
|
|
427
|
+
onopen: (() => void) | null;
|
|
428
|
+
onmessage: ((event: {
|
|
429
|
+
data: unknown;
|
|
430
|
+
}) => void) | null;
|
|
431
|
+
onclose: (() => void) | null;
|
|
432
|
+
onerror: ((event: unknown) => void) | null;
|
|
433
|
+
}
|
|
434
|
+
type WsStatusSourceOptions = {
|
|
435
|
+
/** Server base URL (http/https); the `ws(s)://…/ws` endpoint is derived from it. */
|
|
436
|
+
serverUrl: string;
|
|
437
|
+
/** WebSocket factory — defaults to the global `WebSocket` (browsers, Node ≥ 22). */
|
|
438
|
+
webSocketFactory?: (url: string) => WebSocketLike;
|
|
439
|
+
/** Reconnect backoff floor/ceiling (ms). Defaults: 1_000 / 30_000. */
|
|
440
|
+
reconnectMinMs?: number;
|
|
441
|
+
reconnectMaxMs?: number;
|
|
442
|
+
};
|
|
443
|
+
declare class WsStatusSource {
|
|
444
|
+
#private;
|
|
445
|
+
constructor(options: WsStatusSourceOptions);
|
|
446
|
+
/** Open the connection and begin (re)subscribing. Idempotent. */
|
|
447
|
+
start(): void;
|
|
448
|
+
/** Close the connection and stop reconnecting. */
|
|
449
|
+
stop(): void;
|
|
450
|
+
/** Emit for each pushed status update. Returns an unsubscribe fn. */
|
|
451
|
+
onStatus(cb: (update: SwapStatusUpdate) => void): () => void;
|
|
452
|
+
/**
|
|
453
|
+
* Emit whenever the socket comes (back) up after a disruption — a drop, or a
|
|
454
|
+
* first connect that had to be retried. NOT emitted on a pristine first open:
|
|
455
|
+
* hints were never live before it, so there is no gap to recover from. While
|
|
456
|
+
* the socket was down, pushed transitions were lost for good (the server only
|
|
457
|
+
* snapshots on subscribe), so a consumer should re-verify its world against
|
|
458
|
+
* the chain on this signal.
|
|
459
|
+
*/
|
|
460
|
+
onReconnect(cb: () => void): () => void;
|
|
461
|
+
/**
|
|
462
|
+
* Track these swap ids — subscribe on the socket now if open, else on next open.
|
|
463
|
+
*
|
|
464
|
+
* Enforces the server's per-connection cap locally. The server silently stops
|
|
465
|
+
* adding once it is full (it breaks out of the loop but still acks every id),
|
|
466
|
+
* so sending more would leave us believing ids are subscribed that will never
|
|
467
|
+
* receive an update. Overflow is queued instead and promoted as slots free.
|
|
468
|
+
*/
|
|
469
|
+
subscribe(swapIds: string[]): void;
|
|
470
|
+
/** Stop tracking these swap ids, promoting queued ones into the freed slots. */
|
|
471
|
+
unsubscribe(swapIds: string[]): void;
|
|
472
|
+
}
|
|
473
|
+
|
|
399
474
|
/**
|
|
400
475
|
* The reactive orchestration.
|
|
401
476
|
*
|
|
@@ -533,6 +608,8 @@ type TrackingConfig = {
|
|
|
533
608
|
* remain the source of truth.
|
|
534
609
|
*/
|
|
535
610
|
serverUrl?: string;
|
|
611
|
+
/** WebSocket factory for the hint feed; defaults to the global `WebSocket` (testing). */
|
|
612
|
+
webSocketFactory?: (url: string) => WebSocketLike;
|
|
536
613
|
/** Explicit per-ledger managers, bypassing auto-construction (advanced/testing). */
|
|
537
614
|
managers?: Map<Ledger, ContractManager>;
|
|
538
615
|
/** Ark server URL override; defaults to the mainnet server when unset. */
|
|
@@ -675,6 +752,7 @@ declare class Client {
|
|
|
675
752
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
676
753
|
claim(...args: Parameters<Client$1["claim"]>): ReturnType<Client$1["claim"]>;
|
|
677
754
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
755
|
+
canSponsorClaim(...args: Parameters<Client$1["canSponsorClaim"]>): boolean;
|
|
678
756
|
claimEvmWithSigner(...args: Parameters<Client$1["claimEvmWithSigner"]>): ReturnType<Client$1["claimEvmWithSigner"]>;
|
|
679
757
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
680
758
|
claimViaGasless(...args: Parameters<Client$1["claimViaGasless"]>): ReturnType<Client$1["claimViaGasless"]>;
|
|
@@ -689,6 +767,16 @@ declare class Client {
|
|
|
689
767
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
690
768
|
buildCollabRefundEvmTypedData(...args: Parameters<Client$1["buildCollabRefundEvmTypedData"]>): ReturnType<Client$1["buildCollabRefundEvmTypedData"]>;
|
|
691
769
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
770
|
+
getCollabRefundEvmSignature(...args: Parameters<Client$1["getCollabRefundEvmSignature"]>): ReturnType<Client$1["getCollabRefundEvmSignature"]>;
|
|
771
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
772
|
+
getRehashEvmParams(...args: Parameters<Client$1["getRehashEvmParams"]>): ReturnType<Client$1["getRehashEvmParams"]>;
|
|
773
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
774
|
+
signRehashEvm(...args: Parameters<Client$1["signRehashEvm"]>): ReturnType<Client$1["signRehashEvm"]>;
|
|
775
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
776
|
+
retrySwapWithInvoice(...args: Parameters<Client$1["retrySwapWithInvoice"]>): ReturnType<Client$1["retrySwapWithInvoice"]>;
|
|
777
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
778
|
+
retrySwapWithInvoiceWithSigner(...args: Parameters<Client$1["retrySwapWithInvoiceWithSigner"]>): ReturnType<Client$1["retrySwapWithInvoiceWithSigner"]>;
|
|
779
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
692
780
|
collabRefundEvmSwap(...args: Parameters<Client$1["collabRefundEvmSwap"]>): ReturnType<Client$1["collabRefundEvmSwap"]>;
|
|
693
781
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
694
782
|
submitCollabRefundEvm(...args: Parameters<Client$1["submitCollabRefundEvm"]>): ReturnType<Client$1["submitCollabRefundEvm"]>;
|
|
@@ -718,7 +806,10 @@ declare class Client {
|
|
|
718
806
|
getCoordinatorFundingCallDataPermit2(...args: Parameters<Client$1["getCoordinatorFundingCallDataPermit2"]>): ReturnType<Client$1["getCoordinatorFundingCallDataPermit2"]>;
|
|
719
807
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
720
808
|
getPermit2FundingParamsUnsigned(...args: Parameters<Client$1["getPermit2FundingParamsUnsigned"]>): ReturnType<Client$1["getPermit2FundingParamsUnsigned"]>;
|
|
721
|
-
/**
|
|
809
|
+
/**
|
|
810
|
+
* Fund the swap, then re-verify it against the chain so the lock shows up
|
|
811
|
+
* in the derived actions without waiting on a server hint.
|
|
812
|
+
*/
|
|
722
813
|
fundSwap(...args: Parameters<Client$1["fundSwap"]>): ReturnType<Client$1["fundSwap"]>;
|
|
723
814
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
724
815
|
fundSwapFromSolana(...args: Parameters<Client$1["fundSwapFromSolana"]>): ReturnType<Client$1["fundSwapFromSolana"]>;
|
|
@@ -733,6 +824,8 @@ declare class Client {
|
|
|
733
824
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
734
825
|
fundSwapGasless(...args: Parameters<Client$1["fundSwapGasless"]>): ReturnType<Client$1["fundSwapGasless"]>;
|
|
735
826
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
827
|
+
getEvmFundingExpectation(...args: Parameters<Client$1["getEvmFundingExpectation"]>): ReturnType<Client$1["getEvmFundingExpectation"]>;
|
|
828
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
736
829
|
getSwapDepositorKey(...args: Parameters<Client$1["getSwapDepositorKey"]>): ReturnType<Client$1["getSwapDepositorKey"]>;
|
|
737
830
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
738
831
|
getEvmDepositorKey(...args: Parameters<Client$1["getEvmDepositorKey"]>): ReturnType<Client$1["getEvmDepositorKey"]>;
|
|
@@ -1538,7 +1631,7 @@ type SwapWorkerOptions = {
|
|
|
1538
1631
|
* Resolve once submitted. OMIT for observe-only mode: hints still trigger
|
|
1539
1632
|
* chain re-verifies (fast, cheap derivations), but nothing is ever run.
|
|
1540
1633
|
*/
|
|
1541
|
-
execute?: (swapId: string, actionId: SwapActionId) => Promise<void>;
|
|
1634
|
+
execute?: (swapId: string, actionId: SwapActionId, actions: SwapActions) => Promise<void>;
|
|
1542
1635
|
/**
|
|
1543
1636
|
* Surface an action that needs the user rather than being auto-run — a manual
|
|
1544
1637
|
* `fund`, a refund the user must confirm, or a claim whose retries are spent.
|
|
@@ -1553,83 +1646,16 @@ declare class SwapWorker {
|
|
|
1553
1646
|
constructor(options: SwapWorkerOptions);
|
|
1554
1647
|
/** Watch tracked swaps on the hint feed, wire hints → verify, react to actions. */
|
|
1555
1648
|
start(): void;
|
|
1556
|
-
/** Stop reacting, cancel pending retries, and close the hint feed. */
|
|
1557
|
-
stop(): void;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
/**
|
|
1561
|
-
* A standalone subscriber to the server's swap-status WebSocket (`GET /ws`).
|
|
1562
|
-
*
|
|
1563
|
-
* Holds a single reconnecting connection, tracks a live set of subscribed swap
|
|
1564
|
-
* ids, and emits `{ swapId, status }` on every status transition the server
|
|
1565
|
-
* pushes — including the snapshot the server sends right after a subscribe. This
|
|
1566
|
-
* is a *hint* feed only: it neither trusts nor acts on the status. A consumer is
|
|
1567
|
-
* expected to verify against the chain (the contract managers) before doing
|
|
1568
|
-
* anything with it.
|
|
1569
|
-
*
|
|
1570
|
-
* Protocol (see `swap/src/api/ws.rs`):
|
|
1571
|
-
* → {"op":"subscribe","channel":"swap_status","args":[<uuid>,...]} (≤64 ids/frame)
|
|
1572
|
-
* ← {"channel":"swap_status","data":{"<uuid>":"<status>",...}}
|
|
1573
|
-
* ← {"op":"subscribed",...} (ack) ← {"op":...,"message":...} (error)
|
|
1574
|
-
* A single connection carries up to 256 subscriptions; that cap is enforced
|
|
1575
|
-
* client-side (see {@link WsStatusSource.subscribe}) because the server silently
|
|
1576
|
-
* drops the overflow.
|
|
1577
|
-
*/
|
|
1578
|
-
|
|
1579
|
-
/** A status update pushed by the server for one swap. */
|
|
1580
|
-
type SwapStatusUpdate = {
|
|
1581
|
-
swapId: string;
|
|
1582
|
-
status: SwapStatus;
|
|
1583
|
-
};
|
|
1584
|
-
/** The minimal WebSocket surface the source needs (the global `WebSocket` satisfies it). */
|
|
1585
|
-
interface WebSocketLike {
|
|
1586
|
-
send(data: string): void;
|
|
1587
|
-
close(): void;
|
|
1588
|
-
onopen: (() => void) | null;
|
|
1589
|
-
onmessage: ((event: {
|
|
1590
|
-
data: unknown;
|
|
1591
|
-
}) => void) | null;
|
|
1592
|
-
onclose: (() => void) | null;
|
|
1593
|
-
onerror: ((event: unknown) => void) | null;
|
|
1594
|
-
}
|
|
1595
|
-
type WsStatusSourceOptions = {
|
|
1596
|
-
/** Server base URL (http/https); the `ws(s)://…/ws` endpoint is derived from it. */
|
|
1597
|
-
serverUrl: string;
|
|
1598
|
-
/** WebSocket factory — defaults to the global `WebSocket` (browsers, Node ≥ 22). */
|
|
1599
|
-
webSocketFactory?: (url: string) => WebSocketLike;
|
|
1600
|
-
/** Reconnect backoff floor/ceiling (ms). Defaults: 1_000 / 30_000. */
|
|
1601
|
-
reconnectMinMs?: number;
|
|
1602
|
-
reconnectMaxMs?: number;
|
|
1603
|
-
};
|
|
1604
|
-
declare class WsStatusSource {
|
|
1605
|
-
#private;
|
|
1606
|
-
constructor(options: WsStatusSourceOptions);
|
|
1607
|
-
/** Open the connection and begin (re)subscribing. Idempotent. */
|
|
1608
|
-
start(): void;
|
|
1609
|
-
/** Close the connection and stop reconnecting. */
|
|
1610
|
-
stop(): void;
|
|
1611
|
-
/** Emit for each pushed status update. Returns an unsubscribe fn. */
|
|
1612
|
-
onStatus(cb: (update: SwapStatusUpdate) => void): () => void;
|
|
1613
|
-
/**
|
|
1614
|
-
* Emit whenever the socket comes (back) up after a disruption — a drop, or a
|
|
1615
|
-
* first connect that had to be retried. NOT emitted on a pristine first open:
|
|
1616
|
-
* hints were never live before it, so there is no gap to recover from. While
|
|
1617
|
-
* the socket was down, pushed transitions were lost for good (the server only
|
|
1618
|
-
* snapshots on subscribe), so a consumer should re-verify its world against
|
|
1619
|
-
* the chain on this signal.
|
|
1620
|
-
*/
|
|
1621
|
-
onReconnect(cb: () => void): () => void;
|
|
1622
1649
|
/**
|
|
1623
|
-
*
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
* receive an update. Overflow is queued instead and promoted as slots free.
|
|
1650
|
+
* Put swaps on the hint feed as soon as they are tracked. {@link #onActions}
|
|
1651
|
+
* subscribes a swap when its first action is emitted, but a swap whose leg
|
|
1652
|
+
* cannot be classified yet (an unfunded lock on a chain without `eth_getLogs`)
|
|
1653
|
+
* emits nothing, and without this it would never receive the server hint
|
|
1654
|
+
* that its funding landed.
|
|
1629
1655
|
*/
|
|
1630
1656
|
subscribe(swapIds: string[]): void;
|
|
1631
|
-
/** Stop
|
|
1632
|
-
|
|
1657
|
+
/** Stop reacting, cancel pending retries, and close the hint feed. */
|
|
1658
|
+
stop(): void;
|
|
1633
1659
|
}
|
|
1634
1660
|
|
|
1635
1661
|
/**
|
package/dist/index.js
CHANGED
|
@@ -419,13 +419,10 @@ async function fetchArkadeSpend(source, spentByTxid, paymentHash) {
|
|
|
419
419
|
|
|
420
420
|
// src/contracts/arkade-manager.ts
|
|
421
421
|
function isFunded(vtxo) {
|
|
422
|
-
|
|
423
|
-
return state === "preconfirmed" || state === "settled";
|
|
422
|
+
return !vtxo.isSpent && !vtxo.isSwept;
|
|
424
423
|
}
|
|
425
424
|
function spendTxid(vtxos) {
|
|
426
|
-
const spent = vtxos.find(
|
|
427
|
-
(v) => v.arkTxId || v.spentBy || v.isSpent || v.virtualStatus.state === "spent"
|
|
428
|
-
);
|
|
425
|
+
const spent = vtxos.find((v) => v.arkTxId || v.spentBy || v.isSpent);
|
|
429
426
|
return spent?.arkTxId ?? spent?.spentBy;
|
|
430
427
|
}
|
|
431
428
|
var ArkadeContractManager = class _ArkadeContractManager {
|
|
@@ -1478,6 +1475,17 @@ var SwapWorker = class {
|
|
|
1478
1475
|
)
|
|
1479
1476
|
);
|
|
1480
1477
|
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Put swaps on the hint feed as soon as they are tracked. {@link #onActions}
|
|
1480
|
+
* subscribes a swap when its first action is emitted, but a swap whose leg
|
|
1481
|
+
* cannot be classified yet (an unfunded lock on a chain without `eth_getLogs`)
|
|
1482
|
+
* emits nothing, and without this it would never receive the server hint
|
|
1483
|
+
* that its funding landed.
|
|
1484
|
+
*/
|
|
1485
|
+
subscribe(swapIds) {
|
|
1486
|
+
if (swapIds.length === 0) return;
|
|
1487
|
+
this.#hintSource.subscribe(swapIds);
|
|
1488
|
+
}
|
|
1481
1489
|
/** Stop reacting, cancel pending retries, and close the hint feed. */
|
|
1482
1490
|
stop() {
|
|
1483
1491
|
this.#started = false;
|
|
@@ -1529,7 +1537,7 @@ var SwapWorker = class {
|
|
|
1529
1537
|
const actionId = actions.recommended;
|
|
1530
1538
|
if (!actionId) return;
|
|
1531
1539
|
this.#executing.add(swapId);
|
|
1532
|
-
void execute(swapId, actionId).then(() => this.#cancelRetry(swapId)).catch((error) => {
|
|
1540
|
+
void execute(swapId, actionId, actions).then(() => this.#cancelRetry(swapId)).catch((error) => {
|
|
1533
1541
|
if (needsWallet(error)) {
|
|
1534
1542
|
this.#cancelRetry(swapId);
|
|
1535
1543
|
this.#needsUser.add(swapId);
|
|
@@ -1947,7 +1955,7 @@ function swapToTracked(stored) {
|
|
|
1947
1955
|
token: r.evm_htlc_kind === "native" ? NATIVE_TOKEN_ADDRESS : lockedTokenFallback(r.evm_chain_id, r.network),
|
|
1948
1956
|
// The coordinator created the HTLC on the client's behalf, so it
|
|
1949
1957
|
// is the lock's sender (refund address), as on evm_to_lightning.
|
|
1950
|
-
sender: r.evm_coordinator_address,
|
|
1958
|
+
sender: r.evm_htlc_kind === "native" && r.evm_coordinator_address.toLowerCase() === NATIVE_TOKEN_ADDRESS ? r.client_evm_address ?? void 0 : r.evm_coordinator_address,
|
|
1951
1959
|
timelockSec: r.evm_refund_locktime,
|
|
1952
1960
|
createdAt: r.created_at
|
|
1953
1961
|
}),
|
|
@@ -1997,7 +2005,7 @@ function swapToTracked(stored) {
|
|
|
1997
2005
|
token: r.wbtc_address,
|
|
1998
2006
|
// The coordinator created the HTLC on the client's behalf, so it
|
|
1999
2007
|
// is the lock's sender (refund address).
|
|
2000
|
-
sender: r.evm_coordinator_address,
|
|
2008
|
+
sender: r.evm_htlc_kind === "native" && r.evm_coordinator_address.toLowerCase() === NATIVE_TOKEN_ADDRESS ? r.client_evm_address ?? void 0 : r.evm_coordinator_address,
|
|
2001
2009
|
timelockSec: r.evm_refund_locktime,
|
|
2002
2010
|
createdAt: r.created_at
|
|
2003
2011
|
}),
|
|
@@ -2092,7 +2100,7 @@ function swapToTracked(stored) {
|
|
|
2092
2100
|
// the server claims the client's EVM HTLC
|
|
2093
2101
|
expectedSats: r.evm_expected_sats,
|
|
2094
2102
|
token: r.wbtc_address,
|
|
2095
|
-
sender: r.evm_coordinator_address,
|
|
2103
|
+
sender: r.evm_htlc_kind === "native" && r.evm_coordinator_address.toLowerCase() === NATIVE_TOKEN_ADDRESS ? r.client_evm_address ?? void 0 : r.evm_coordinator_address,
|
|
2096
2104
|
// the coordinator locks on the client's behalf
|
|
2097
2105
|
timelockSec: r.evm_refund_locktime,
|
|
2098
2106
|
createdAt: r.created_at
|
|
@@ -2714,6 +2722,9 @@ var Client = class {
|
|
|
2714
2722
|
return this.#legacy.claim(...args);
|
|
2715
2723
|
}
|
|
2716
2724
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2725
|
+
canSponsorClaim(...args) {
|
|
2726
|
+
return this.#legacy.canSponsorClaim(...args);
|
|
2727
|
+
}
|
|
2717
2728
|
claimEvmWithSigner(...args) {
|
|
2718
2729
|
return this.#legacy.claimEvmWithSigner(...args);
|
|
2719
2730
|
}
|
|
@@ -2742,6 +2753,26 @@ var Client = class {
|
|
|
2742
2753
|
return this.#legacy.buildCollabRefundEvmTypedData(...args);
|
|
2743
2754
|
}
|
|
2744
2755
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2756
|
+
getCollabRefundEvmSignature(...args) {
|
|
2757
|
+
return this.#legacy.getCollabRefundEvmSignature(...args);
|
|
2758
|
+
}
|
|
2759
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
2760
|
+
getRehashEvmParams(...args) {
|
|
2761
|
+
return this.#legacy.getRehashEvmParams(...args);
|
|
2762
|
+
}
|
|
2763
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
2764
|
+
signRehashEvm(...args) {
|
|
2765
|
+
return this.#legacy.signRehashEvm(...args);
|
|
2766
|
+
}
|
|
2767
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
2768
|
+
retrySwapWithInvoice(...args) {
|
|
2769
|
+
return this.#legacy.retrySwapWithInvoice(...args);
|
|
2770
|
+
}
|
|
2771
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
2772
|
+
retrySwapWithInvoiceWithSigner(...args) {
|
|
2773
|
+
return this.#legacy.retrySwapWithInvoiceWithSigner(...args);
|
|
2774
|
+
}
|
|
2775
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
2745
2776
|
collabRefundEvmSwap(...args) {
|
|
2746
2777
|
return this.#legacy.collabRefundEvmSwap(...args);
|
|
2747
2778
|
}
|
|
@@ -2817,9 +2848,14 @@ var Client = class {
|
|
|
2817
2848
|
getPermit2FundingParamsUnsigned(...args) {
|
|
2818
2849
|
return this.#legacy.getPermit2FundingParamsUnsigned(...args);
|
|
2819
2850
|
}
|
|
2820
|
-
/**
|
|
2821
|
-
|
|
2822
|
-
|
|
2851
|
+
/**
|
|
2852
|
+
* Fund the swap, then re-verify it against the chain so the lock shows up
|
|
2853
|
+
* in the derived actions without waiting on a server hint.
|
|
2854
|
+
*/
|
|
2855
|
+
async fundSwap(...args) {
|
|
2856
|
+
const result = await this.#legacy.fundSwap(...args);
|
|
2857
|
+
this.#reverifyAfterFunding(args[0]);
|
|
2858
|
+
return result;
|
|
2823
2859
|
}
|
|
2824
2860
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2825
2861
|
fundSwapFromSolana(...args) {
|
|
@@ -2846,6 +2882,10 @@ var Client = class {
|
|
|
2846
2882
|
return this.#legacy.fundSwapGasless(...args);
|
|
2847
2883
|
}
|
|
2848
2884
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2885
|
+
getEvmFundingExpectation(...args) {
|
|
2886
|
+
return this.#legacy.getEvmFundingExpectation(...args);
|
|
2887
|
+
}
|
|
2888
|
+
/** Delegated to the legacy client (migration checkpoint). */
|
|
2849
2889
|
getSwapDepositorKey(...args) {
|
|
2850
2890
|
return this.#legacy.getSwapDepositorKey(...args);
|
|
2851
2891
|
}
|
|
@@ -3046,11 +3086,37 @@ var Client = class {
|
|
|
3046
3086
|
for (const swap of swaps) {
|
|
3047
3087
|
try {
|
|
3048
3088
|
await tracker.track(swap);
|
|
3089
|
+
if (tracker.trackedSwapIds().includes(swap.swapId))
|
|
3090
|
+
this.#worker?.subscribe([swap.swapId]);
|
|
3049
3091
|
} catch (error) {
|
|
3050
3092
|
console.warn(`Client: tracking swap ${swap.swapId} failed:`, error);
|
|
3051
3093
|
}
|
|
3052
3094
|
}
|
|
3053
3095
|
}
|
|
3096
|
+
/**
|
|
3097
|
+
* Re-verify a swap against the chain right after this client funded it.
|
|
3098
|
+
* The funding is mined when `fundSwap` resolves, but nothing else tells the
|
|
3099
|
+
* tracker so: a server hint arrives only after the server's own monitor
|
|
3100
|
+
* sees the lock (on Rootstock, a 30 s poll plus confirmations), and the
|
|
3101
|
+
* tracker's at-risk poll does not re-read an `absent` client leg. One
|
|
3102
|
+
* targeted read moves the swap to `clientfunded` at once. Fire-and-forget:
|
|
3103
|
+
* the funding succeeded whatever the re-verify does, and a swap that is not
|
|
3104
|
+
* tracked is a no-op.
|
|
3105
|
+
*
|
|
3106
|
+
* Chain-verified tracking only: a hints-only tracker has no chain to read,
|
|
3107
|
+
* its `applyHint` re-fetches the server's stored status, which lags the
|
|
3108
|
+
* lock by the same server-side delay this works around.
|
|
3109
|
+
*/
|
|
3110
|
+
#reverifyAfterFunding(swapId) {
|
|
3111
|
+
if (typeof swapId !== "string" || !(this.#tracker instanceof SwapTracker))
|
|
3112
|
+
return;
|
|
3113
|
+
void this.#tracker.applyHint(swapId).catch((error) => {
|
|
3114
|
+
console.warn(
|
|
3115
|
+
`Client: re-verify after funding ${swapId} failed (the tracker's next read catches up):`,
|
|
3116
|
+
error
|
|
3117
|
+
);
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3054
3120
|
/**
|
|
3055
3121
|
* Wire the server status WebSocket into a {@link SwapWorker} whenever a server
|
|
3056
3122
|
* URL is configured: pushed status transitions trigger a targeted chain
|
|
@@ -3063,7 +3129,10 @@ var Client = class {
|
|
|
3063
3129
|
const serverUrl = this.#tracking.serverUrl ?? legacyBaseUrl(this.#legacy);
|
|
3064
3130
|
if (!serverUrl) return;
|
|
3065
3131
|
const autoClaim = this.#tracking.autoClaim;
|
|
3066
|
-
const hintSource = new WsStatusSource({
|
|
3132
|
+
const hintSource = new WsStatusSource({
|
|
3133
|
+
serverUrl,
|
|
3134
|
+
webSocketFactory: this.#tracking.webSocketFactory
|
|
3135
|
+
});
|
|
3067
3136
|
const worker = new SwapWorker({
|
|
3068
3137
|
tracker,
|
|
3069
3138
|
hintSource,
|