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 txs | Method | First scan | Second scan | HTTP requests (1st) | HTTP requests (2nd) |
|---|---|---|---|---|---|
| 2 | Esplora | 3.2s | 2.1s | 68 | 62 |
| 2 | Waterfalls | 0.4s | 0.2s | 7 | 2 |
| 65 | Esplora | 13.1s | 7.1s | 294 | 164 |
| 65 | Waterfalls | 1.8s | 0.1s | 70 | 2 |
| 6,442 | Esplora | 529s | 261s | 18,894 | 6,504 |
| 6,442 | Waterfalls | 198s | 1.2s | 6,454 | 10 |
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:
| Network | URL |
|---|---|
| Liquid mainnet | https://waterfalls.liquidwebwallet.org/liquid |
| Liquid testnet | https://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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
descriptor | string | Yes (or addresses) | CT descriptor (URL encoded) | |
addresses | string | No | Comma separated addresses (alternative to descriptor) | |
page | integer | No | 0 | Pagination |
to_index | integer | No | 0 | Max derivation index |
utxo_only | boolean | No | false | Only 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:
| Endpoint | Description |
|---|---|
GET /blocks/tip/hash | Current chain tip hash |
GET /block-height/<height> | Block hash at height |
GET /block/<hash>/header | Block header |
GET /tx/<txid>/raw | Raw transaction |
GET /address/<address>/txs | Address transaction history |
GET /fee-estimates | Fee rate estimates |
POST /tx | Broadcast 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:latestDisk requirements for the Waterfalls database:
| Network | Blocks dir | Waterfalls 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
| Scenario | Recommendation |
|---|---|
| Wallet with few transactions | Either works fine |
| Wallet with hundreds/thousands of txs | Waterfalls (much faster) |
| You need address level queries | Esplora |
| You need asset metadata | Esplora |
| You want fastest possible sync | Waterfalls |
| You want to minimize server trust | Self 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?