Waterfalls

Waterfalls is a server that provides fast wallet scanning for Liquid (and Bitcoin). Instead of querying each address individually like traditional Esplora or Electrum scanning, you send your descriptor and get back the complete wallet history in a single request.

The problem Waterfalls solves

Traditional wallet sync works like this:

sequenceDiagram
  participant W as Your Wallet
  participant E as Esplora

  W->>W: Derive address 1
  W->>E: Get history for address 1
  E-->>W: History
  W->>W: Derive address 2
  W->>E: Get history for address 2
  E-->>W: History
  W->>W: Derive address N
  W->>E: Get history for address N
  E-->>W: History
  Note over W,E: N roundtrips for N addresses

For a wallet with hundreds of addresses, this means hundreds of sequential HTTP requests. Slow, and likely to hit rate limits.

Waterfalls replaces this with:

sequenceDiagram
  participant W as Your Wallet
  participant WF as Waterfalls

  W->>WF: Send descriptor
  WF->>WF: Derive all addresses, fetch all history
  WF-->>W: Complete wallet history
  Note over W,WF: 1 roundtrip

Performance

Real world benchmarks comparing traditional Esplora scanning vs. Waterfalls:

Wallet txsMethodFirst scanSecond scanHTTP requests (1st)HTTP requests (2nd)
2Esplora3.2s2.1s6862
2Waterfalls0.4s0.2s72
65Esplora13.1s7.1s294164
65Waterfalls1.8s0.1s702
6,442Esplora529s261s18,8946,504
6,442Waterfalls198s1.2s6,45410

The difference is dramatic. For the 6,442 transaction wallet, the second sync (incremental) goes from 261 seconds to 1.2 seconds.

Public instance

Blockstream operates a public Waterfalls instance:

NetworkURL
Liquid mainnethttps://waterfalls.liquidwebwallet.org/liquid
Liquid testnethttps://waterfalls.liquidwebwallet.org/liquidtestnet

Using Waterfalls with LWK

LWK has built in Waterfalls support. The language bindings differ slightly:

Rust

use lwk_wollet::WaterfallsClient;

let client = WaterfallsClient::new(
    "https://waterfalls.liquidwebwallet.org/liquid"
).unwrap();

wollet.full_scan(&client).unwrap();

let balance = wollet.balance().unwrap();
println!("Balances: {:?}", balance);

Python

client = lwk.WaterfallsClient("https://waterfalls.liquidwebwallet.org/liquid")
wollet.full_scan(client)

balance = wollet.balance()

JavaScript

In lwk_wasm there is no separate WaterfallsClient class. Waterfalls mode is enabled by passing waterfalls = true (3rd argument) to EsploraClient, pointing at a Waterfalls server:


const client = new EsploraClient(
  network,
  "https://waterfalls.liquidwebwallet.org/liquidtestnet/api", // testnet
  true, // waterfalls mode on
  4,    // concurrency
  false // utxo-only mode
);

const update = await client.fullScan(wollet);
if (update) {
  wollet.applyUpdate(update);
}

const balance = wollet.balance();

For mainnet use https://waterfalls.liquidwebwallet.org/liquid/api.

Drop in replacement for the Esplora/Electrum client. Same full_scan call, faster results.

API reference

Wallet scan (v2, recommended)

GET /v2/waterfalls?descriptor=<descriptor>&page=<page>&to_index=<max_index>&utxo_only=<bool>

Parameters:

ParameterTypeRequiredDefaultDescription
descriptorstringYes (or addresses)CT descriptor (URL encoded)
addressesstringNoComma separated addresses (alternative to descriptor)
pageintegerNo0Pagination
to_indexintegerNo0Max derivation index
utxo_onlybooleanNofalseOnly return unspent outputs

Example:

curl -s "https://waterfalls.liquidwebwallet.org/liquid/v2/waterfalls?descriptor=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("elwpkh(xpub6Bem.../*)"))' )"

Response:

{
  "txs_seen": {
    "descriptor_or_addresses": [
      {
        "txid": "abc123...",
        "height": 12345,
        "block_hash": "def456...",
        "block_timestamp": 1700000000,
        "v": 1
      }
    ]
  },
  "page": 0,
  "tip": "current_tip_hash"
}

UTXO only mode

If you only need current balances (not full transaction history), use utxo_only=true. This is faster and returns less data:

curl "https://waterfalls.liquidwebwallet.org/liquid/v2/waterfalls?descriptor=...&utxo_only=true"

Last used index

GET /v1/last_used_index?descriptor=<descriptor>

Returns the highest derivation index that has been used. Useful for quickly determining the next unused address without downloading full history:

{
  "external": 42,
  "internal": 15,
  "tip": "current_tip_hash"
}

Other endpoints

Waterfalls also mirrors some Esplora endpoints:

EndpointDescription
GET /blocks/tip/hashCurrent chain tip hash
GET /block-height/<height>Block hash at height
GET /block/<hash>/headerBlock header
GET /tx/<txid>/rawRaw transaction
GET /address/<address>/txsAddress transaction history
GET /fee-estimatesFee rate estimates
POST /txBroadcast transaction

Privacy considerations

When you send a descriptor to Waterfalls, the server can derive all your addresses. This is the same information it would get if you queried each address individually, but now it also knows those addresses are related (from the same descriptor).

For Liquid specifically, this is less of a concern because Confidential Transactions mean the server can see which addresses have transactions, but it cannot see the amounts or asset types (the blinding keys are not sent).

For maximum privacy, self host Waterfalls.

Self hosting

Waterfalls is open source and can be run with Docker:

# Pull and run for Liquid mainnet
docker run -p 3100:3100 -e NETWORK=liquid waterfalls:latest

# Or use Esplora as the data source instead of a local node
docker run -p 3100:3100 -e NETWORK=liquid -e USE_ESPLORA=true waterfalls:latest

Disk requirements for the Waterfalls database:

NetworkBlocks dirWaterfalls DB
Liquid mainnet~38 GB~876 MB
Liquid testnet~4.1 GB~258 MB

Much smaller than running a full Esplora indexer.

When to use Waterfalls vs. Esplora

ScenarioRecommendation
Wallet with few transactionsEither works fine
Wallet with hundreds/thousands of txsWaterfalls (much faster)
You need address level queriesEsplora
You need asset metadataEsplora
You want fastest possible syncWaterfalls
You want to minimize server trustSelf host either

For most exchanges, the best setup is: Waterfalls for wallet sync + Esplora for everything else (tx lookups, broadcasts, asset info).

Next steps

  • Supporting AMP2 Assets: if you want to support issuer controlled assets
  • Go Live Checklist: ready for production?

The Liquid Network is a Bitcoin layer-2 enabling the issuance of security tokens and other digital assets.

© 2023 Liquid Network
All rights reserved.

Feedback and Content Requests

We'd be happy to hear your suggestions on how we can improve this site.

BuildOnL2 Community

The official BuildOnL2 community lives
at community.liquid.net. Join us and build the future of Bitcoin on Liquid.

Telegram

Community-driven telegram group where
most of the Liquid developers hang out.
Go to t.me/liquid_devel to join.