npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@satora/escrow-client

v0.0.3

Published

High-level escrow flows (funding/withdrawing) bundling @satora/escrow with an injected swap client

Readme

@satora/escrow-client

High-level escrow flows for Arkade: fund a 2-of-2 escrow from Lightning, and withdraw a released payout to Lightning or to L1. It bundles @satora/escrow (the escrow primitives, all re-exported) with the escrow monitor, and an injected swap client.

You set up two things — a Satora swap client and an EscrowClient — and this README walks through both from scratch. Everything else (escrow primitives, the monitor) comes from this package.

Install

npm install @satora/escrow-client

Peer dependencies (you provide these):

npm install @arkade-os/sdk @satora/swap
  • @arkade-os/sdk — Arkade providers, repositories, and the Wallet used for withdrawals.
  • @satora/swap — the Satora swap client: the Lightning/L1 on/off-ramp. You build one (shown below) and inject it; escrow-client only imports its type, so it carries none of that bundle's runtime weight.

Setup

Two pieces: a swap client and an EscrowClient.

1. Build the swap client

import { Client } from "@satora/swap";

// The minimal form generates a fresh ephemeral wallet on build(). For anything
// real, persist it — pass .withMnemonic("abandon abandon …") (or a storage
// backend) so pending swaps survive restarts.
const swap = await Client.builder()
  .withBaseUrl("https://api.satora.io") // the Satora API
  .build();

2. Build the EscrowClient

It needs the swap client plus Arkade providers and repositories. Create one for the lifetime of your app.

import {
  RestArkProvider,
  RestIndexerProvider,
  InMemoryContractRepository,
  InMemoryWalletRepository,
  networks,
} from "@arkade-os/sdk";
import { EscrowClient } from "@satora/escrow-client";

const ARK_URL = "https://master.arkade.sh"; // the Arkade server (serves Arkade + indexer)

const arkProvider = new RestArkProvider(ARK_URL);
const indexerProvider = new RestIndexerProvider(ARK_URL);

const escrowClient = await EscrowClient.create({
  swap,
  arkProvider,
  indexerProvider,
  contractRepository: new InMemoryContractRepository(),
  walletRepository: new InMemoryWalletRepository(),
});

// Resolve the network from the Arkade server (used for address derivation below).
const info = await arkProvider.getInfo();
const network = networks[info.network as keyof typeof networks];

Works the same in the browser — build the providers and the swap client there (the SDK ships browser storage backends) and pass them in.

Fund an escrow from Lightning

Describe the escrow you want to fund as EscrowScriptOptions (x-only 32-byte pubkeys; exitTimelock is the Arkade server-mandated CSV). fundFromLightning derives the escrow address, starts watching it, and creates a Lightning→Arkade swap whose payout claims into that escrow.

import type { EscrowScriptOptions } from "@satora/escrow-client"; // re-exported from @satora/escrow

const escrow: EscrowScriptOptions = {
  sellerPubKey,            // Uint8Array(32)
  arbiterPubKey,           // Uint8Array(32)
  arkadeServerPubKey,               // Uint8Array(32) — the Arkade server's signer pubkey
  exitTimelock: {type: "blocks", value: 144n},
};

const handle = await escrowClient.fundFromLightning({
  escrow,
  network,
  amountSats: 50_000, // sats to receive at the escrow
});

console.log("pay this invoice:", handle.invoice);
console.log("escrow address:", handle.escrowAddress);

// After the invoice is paid: claim the swap into the escrow and wait for the
// VTXO to land (rejects on timeout).
const funded = await handle.awaitFunded(120_000);
console.log("escrow funded:", funded.contract.script);

handle is { swapId, invoice, escrowAddress, awaitFunded(timeoutMs?) }.

Withdraw a released payout

Once the escrow has been cooperatively released to the recipient's wallet, the payout sits as a normal VTXO in their @arkade-os/sdk Wallet. You hand that Wallet to the withdrawal methods — build it from the recipient's key and the same providers:

import { Wallet, SingleKey } from "@arkade-os/sdk";

const wallet = await Wallet.create({
  identity: SingleKey.fromPrivateKey(recipientSecretKey), // Uint8Array(32)
  arkProvider,
  indexerProvider,
  // ...onchain provider + repositories as your environment needs
});

Then withdraw it — either with the smart withdraw (which auto-routes by inspecting the destination) or with a specific method.

Smart withdraw (auto-route)

withdraw figures out where destination points and dispatches accordingly:

const result = await escrowClient.withdraw({
  wallet,
  destination,        // BOLT11 / LNURL / user@host → Lightning
                      // ark1… / tark1…             → Arkade transfer
                      // bc1… / tb1… / 1…           → L1 offboard
  amountSats,         // required for Arkade + LNURL/address; optional for L1; ignored for BOLT11
});

result.txid;          // present on every branch
if (result.method === "lightning") result.swapId; // lightning also has swapId + sourceAmountSats

result is discriminated by method ("lightning" | "l1" | "arkade"). Use the specific methods below if you already know the destination type.

To Lightning

destination may be a BOLT11 invoice, an LNURL (lnurl1…), or a Lightning address (user@host). For LNURL / address the swap backend resolves and negotiates the invoice, so pass amountSats (what the recipient receives); for a BOLT11 invoice the amount is in the invoice and amountSats is ignored.

// Optional: quote the recipient amount for a full-payout withdrawal
// (payout minus the swap fee) so you don't have to do fee math.
// `availableSats` is the spendable payout in your wallet.
const {recipientSats} = await escrowClient.quoteLightningWithdrawal(availableSats);

const {swapId, fundingTxid, sourceAmountSats} =
  await escrowClient.withdrawToLightning({
    wallet,                                  // @arkade-os/sdk Wallet holding the payout
    destination: "[email protected]",   // invoice | lnurl1… | user@host
    amountSats: recipientSats,               // required for LNURL / address
  });

sourceAmountSats is what was spent from the payout (recipient amount + swap fee); the swap server then pays the recipient.

To L1 (onchain)

A collaborative Arkade offboard (settlement round) to an onchain address:

const settlementTxid = await escrowClient.withdrawToL1({
  wallet,
  destinationAddress: "bc1q…",
  // amountSats?: bigint  // omit to offboard the whole payout
});

To another Arkade address

A plain offchain Arkade transfer — the funds stay on Arkade, so it's the cheapest and fastest withdrawal (no swap, no settlement round):

const arkTxid = await escrowClient.withdrawToArkade({
  wallet,
  destinationAddress: "ark1…", // or tark1… off mainnet
  amountSats: 50_000,
});

Escrow primitives

@satora/escrow-client re-exports everything from @satora/escrow, so you can build/verify/sign escrow transactions and access the monitor without a second import:

import {
  EscrowVtxoScript,        // 2-of-2 escrow VtxoScript
  buildEscrowReleaseTx,    // build the cooperative release Arkade transaction
  signEscrowArkTx,         // sign a release as a co-party
  verifyReleaseArkTx,      // verify a release pays the agreed outputs
} from "@satora/escrow-client";

// The underlying monitor (onFunded/onReleased, watch, listEscrows):
escrowClient.escrowMonitor;

Cleanup

escrowClient.dispose(); // stop watching, clear listeners

API summary

| Method | Purpose | | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | EscrowClient.create(config) | Construct with { swap, arkProvider, indexerProvider, contractRepository, walletRepository }. | | fundFromLightning({ escrow, network, amountSats }) | Create a LN→escrow swap; returns { swapId, invoice, escrowAddress, awaitFunded() }. | | withdraw({ wallet, destination, amountSats? }) | Smart withdrawal — auto-routes to Lightning / L1 / Arkade by destination. Returns { method, txid, … }. | | quoteLightningWithdrawal(sourceAmountSats) | { recipientSats, sourceSats } — recipient amount after the swap fee. | | withdrawToLightning({ wallet, destination, amountSats? }) | Withdraw the payout to a BOLT11 / LNURL / Lightning address. | | withdrawToL1({ wallet, destinationAddress, amountSats? }) | Withdraw the payout onchain via collaborative offboard. | | withdrawToArkade({ wallet, destinationAddress, amountSats }) | Withdraw the payout to another Arkade address (offchain Arkade transfer). | | escrowMonitor | The underlying EscrowMonitor. | | dispose() | Release monitor resources. |