Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Multi-provider, multi-chain Ethereum JSON-RPC proxy for HTTP and WebSocket
- Intelligent routing strategies: fastest, round-robin, latency-weighted
- Intelligent routing strategies: fastest, load-balanced, latency-weighted
- Per-method, per-transport latency benchmarking
- Profile system for isolated routing configurations (dev/staging/prod/multi-tenant)
- Circuit breakers with per-provider, per-transport state
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Different providers excel at different workloads (hot reads vs archival queries
## Features

- **Multi-provider, multi-chain** Ethereum JSON-RPC proxy for **HTTP + WebSocket**
- **Routing strategies**: `fastest`, `round-robin`, `latency-weighted`, plus provider override routes
- **Routing strategies**: `fastest`, `load-balanced`, `latency-weighted`, plus provider override routes
- **Method-aware benchmarking**: latency tracked per **provider × method × transport**
- **Resilience**: circuit breakers, retries, and transport-aware failover
- **WebSocket subscriptions**: multiplexing with optional gap-filling via HTTP on upstream failure
Expand All @@ -77,7 +77,7 @@ HTTP (POST):

- `/rpc/:chain` (default strategy)
- `/rpc/fastest/:chain`
- `/rpc/round-robin/:chain`
- `/rpc/load-balanced/:chain`
- `/rpc/latency-weighted/:chain`
- `/rpc/provider/:provider_id/:chain` (provider override)

Expand Down
4 changes: 2 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ const DraggableNetworkViewport = {
// Endpoint Selector Hook for Chain Details
const EndpointSelector = {
mounted() {
this.selectedStrategy = "round-robin"; // default strategy
this.selectedStrategy = "load-balanced"; // default strategy
this.selectedProvider = null; // no provider selected by default
this.mode = "strategy"; // 'strategy' or 'provider'
this.selectedProviderSupportsWs = false; // default to false
Expand Down Expand Up @@ -1069,7 +1069,7 @@ const EndpointSelector = {

if (this.mode === "strategy" && this.selectedStrategy) {
const descriptions = {
"round-robin":
"load-balanced":
"Distributes requests evenly across all available providers — good for general purpose workloads",
"latency-weighted":
"Load balanced favoring faster providers — good for high-throughput workloads like indexing and backfilling",
Expand Down
4 changes: 2 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ config :lasso, LassoWeb.Endpoint,
secret_key_base: "YourSecretKeyBaseHere" <> String.duplicate("a", 32)

# Default provider selection strategy
# Options: :fastest, :round_robin, :latency_weighted
config :lasso, :provider_selection_strategy, :round_robin
# Options: :fastest, :load_balanced, :latency_weighted
config :lasso, :provider_selection_strategy, :load_balanced

# Default HTTP client adapter
config :lasso, :http_client, Lasso.RPC.Transport.HTTP.Client.Finch
Expand Down
213 changes: 203 additions & 10 deletions config/profiles/default.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
name: Lasso Public
name: Lasso Free
slug: default
rps_limit: 100
burst_limit: 500
---
# Lasso RPC Profile Configuration
# Frontmatter: Profile metadata (name, slug)
Expand Down Expand Up @@ -61,12 +63,14 @@ chains:
url: "https://eth.drpc.org"
ws_url: "wss://eth.drpc.org"
archival: true
subscribe_new_heads: true

- id: "ethereum_publicnode"
name: "PublicNode Ethereum"
priority: 3
url: "https://ethereum-rpc.publicnode.com"
ws_url: "wss://ethereum-rpc.publicnode.com"
subscribe_new_heads: true
archival: false # PHE: Returns blocks but not historical logs (inverted index error)

# Temporarily disable: Uses a proxied backend with some nodes using PHE and some not (create inconsistency in log responses)
Expand Down Expand Up @@ -97,18 +101,23 @@ chains:
url: "https://eth.llamarpc.com"
ws_url: "wss://eth.llamarpc.com"
archival: false # Mixed PHE nodes - default to false to avoid inverted index errors
subscribe_new_heads: false

- id: "ethereum_blockpi"
name: "BlockPI Ethereum"
priority: 7
url: "https://ethereum.public.blockpi.network/v1/rpc/public"
archival: true
adapter_config:
max_block_range: 1024

- id: "ethereum_nodies"
name: "Nodies Ethereum"
priority: 8
url: "https://ethereum-public.nodies.app"
archival: true
adapter_config:
max_block_range: 500

- id: "ethereum_onfinality"
name: "OnFinality Ethereum"
Expand Down Expand Up @@ -178,14 +187,15 @@ chains:

ui-topology:
color: "#0052FF"
size: md
size: lg

providers:
- id: "base_llamarpc"
name: "LlamaRPC Base"
priority: 2
url: "https://base.llamarpc.com"
ws_url: "wss://base.llamarpc.com"
subscribe_new_heads: true
archival: true

- id: "base_official"
Expand All @@ -199,13 +209,17 @@ chains:
priority: 4
url: "https://base.publicnode.com"
ws_url: "wss://base.publicnode.com"
subscribe_new_heads: false
archival: true

- id: "base_drpc"
name: "dRPC Base"
priority: 5
type: "public"
url: "https://base.drpc.org"
ws_url: "wss://base.drpc.org"
subscribe_new_heads: false
api_key_required: false
archival: true

- id: "base_lava"
Expand Down Expand Up @@ -239,7 +253,7 @@ chains:

ui-topology:
color: "#28A0F0"
size: md
size: lg

providers:
- id: "arbitrum_drpc"
Expand All @@ -248,6 +262,7 @@ chains:
url: "https://arbitrum.drpc.org"
ws_url: "wss://arbitrum.drpc.org"
archival: true
subscribe_new_heads: false

- id: "arbitrum_lava"
name: "Lava Arbitrum"
Expand All @@ -261,7 +276,7 @@ chains:
url: "https://arbitrum-one-rpc.publicnode.com"
ws_url: "wss://arbitrum-one-rpc.publicnode.com"
archival: true
subscribe_new_heads: false
subscribe_new_heads: true

- id: "arbitrum_meowrpc"
name: "Meow RPC Arbitrum"
Expand All @@ -274,9 +289,187 @@ chains:
priority: 6
url: "https://arb-one.api.pocket.network"
archival: true
# Tuning guidelines:
# L1: probe_interval_ms ~12s, max_lag_blocks 1-2, new_heads_timeout_ms ~35-42s
# L2: probe_interval_ms ~3-4s, max_lag_blocks 3-5, new_heads_timeout_ms ~15-20s
# Testnets: Use more lenient thresholds
#
# Provider adapter_config: See docs/ADAPTERS.md for provider-specific options

# ──────────────────────────────────────────────────────────────────────────
# ETHEREUM SEPOLIA (Testnet)
# ──────────────────────────────────────────────────────────────────────────
ethereum-sepolia:
chain_id: 11155111
name: "Ethereum Sepolia"
block_time_ms: 12000

monitoring:
probe_interval_ms: 15000
lag_alert_threshold_blocks: 5

selection:
max_lag_blocks: 3
archival_threshold: 128

websocket:
subscribe_new_heads: true
new_heads_timeout_ms: 42000
failover:
max_backfill_blocks: 50
backfill_timeout_ms: 30000

ui-topology:
color: "#627EEA"
size: sm

providers:
- id: "sepolia_drpc"
name: "dRPC Sepolia"
priority: 2
url: "https://sepolia.drpc.org"
ws_url: "wss://sepolia.drpc.org"
subscribe_new_heads: true
archival: true

- id: "sepolia_publicnode"
name: "PublicNode Sepolia"
priority: 3
url: "https://ethereum-sepolia-rpc.publicnode.com"
ws_url: "wss://ethereum-sepolia-rpc.publicnode.com"
subscribe_new_heads: true
archival: false

- id: "sepolia_1rpc"
name: "1RPC Sepolia"
priority: 4
url: "https://1rpc.io/sepolia"
archival: false

- id: "sepolia_onfinality"
name: "OnFinality Sepolia"
priority: 5
url: "https://eth-sepolia.api.onfinality.io/public"
archival: true

- id: "sepolia_nodies"
name: "Nodies Sepolia"
priority: 6
url: "https://ethereum-sepolia-public.nodies.app"
archival: true
adapter_config:
max_block_range: 500

# ──────────────────────────────────────────────────────────────────────────
# BASE SEPOLIA (Testnet, L2)
# ──────────────────────────────────────────────────────────────────────────
base-sepolia:
chain_id: 84532
name: "Base Sepolia"
block_time_ms: 2000

monitoring:
probe_interval_ms: 5000
lag_alert_threshold_blocks: 5

selection:
max_lag_blocks: 5
archival_threshold: 750

websocket:
subscribe_new_heads: true
new_heads_timeout_ms: 20000
failover:
max_backfill_blocks: 50
backfill_timeout_ms: 30000

ui-topology:
color: "#0052FF"
size: sm

providers:
- id: "base_sepolia_drpc"
name: "dRPC Base Sepolia"
priority: 2
url: "https://base-sepolia.drpc.org"
ws_url: "wss://base-sepolia.drpc.org"
subscribe_new_heads: true
archival: true

- id: "base_sepolia_publicnode"
name: "PublicNode Base Sepolia"
priority: 3
url: "https://base-sepolia-rpc.publicnode.com"
ws_url: "wss://base-sepolia-rpc.publicnode.com"
subscribe_new_heads: true
archival: true

- id: "base_sepolia_official"
name: "Base Official Sepolia"
priority: 4
url: "https://sepolia.base.org"
archival: true

- id: "base_sepolia_onfinality"
name: "OnFinality Base Sepolia"
priority: 5
url: "https://base-sepolia.api.onfinality.io/public"
archival: true

- id: "base_sepolia_nodies"
name: "Nodies Base Sepolia"
priority: 6
url: "https://base-sepolia-public.nodies.app"
archival: true
adapter_config:
max_block_range: 500

# ──────────────────────────────────────────────────────────────────────────
# ARBITRUM SEPOLIA (Testnet, L2)
# ──────────────────────────────────────────────────────────────────────────
arbitrum-sepolia:
chain_id: 421614
name: "Arbitrum Sepolia"
block_time_ms: 250

monitoring:
probe_interval_ms: 2000
lag_alert_threshold_blocks: 15

selection:
max_lag_blocks: 10
archival_threshold: 6000

websocket:
subscribe_new_heads: true
new_heads_timeout_ms: 15000
failover:
max_backfill_blocks: 50
backfill_timeout_ms: 30000

ui-topology:
color: "#28A0F0"
size: sm

providers:
- id: "arb_sepolia_drpc"
name: "dRPC Arbitrum Sepolia"
priority: 2
url: "https://arbitrum-sepolia.drpc.org"
ws_url: "wss://arbitrum-sepolia.drpc.org"
subscribe_new_heads: true
archival: true

- id: "arb_sepolia_publicnode"
name: "PublicNode Arbitrum Sepolia"
priority: 3
url: "https://arbitrum-sepolia-rpc.publicnode.com"
ws_url: "wss://arbitrum-sepolia-rpc.publicnode.com"
subscribe_new_heads: true
archival: true

- id: "arb_sepolia_official"
name: "Arbitrum Official Sepolia"
priority: 4
url: "https://sepolia-rollup.arbitrum.io/rpc"
archival: true

- id: "arb_sepolia_onfinality"
name: "OnFinality Arbitrum Sepolia"
priority: 5
url: "https://arbitrum-sepolia.api.onfinality.io/public"
archival: true
6 changes: 3 additions & 3 deletions docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ All HTTP RPC endpoints accept `POST` requests with `Content-Type: application/js
POST /rpc/:chain
```

Routes using the default strategy (configurable, defaults to `:round_robin`).
Routes using the default strategy (configurable, defaults to `:load_balanced`).

**Example:**

Expand All @@ -26,7 +26,7 @@ curl -X POST http://localhost:4000/rpc/ethereum \

```
POST /rpc/fastest/:chain
POST /rpc/round-robin/:chain
POST /rpc/load-balanced/:chain
POST /rpc/latency-weighted/:chain
```

Expand All @@ -46,7 +46,7 @@ All routes above are available under a profile namespace:
```
POST /rpc/profile/:profile/:chain
POST /rpc/profile/:profile/fastest/:chain
POST /rpc/profile/:profile/round-robin/:chain
POST /rpc/profile/:profile/load-balanced/:chain
POST /rpc/profile/:profile/latency-weighted/:chain
POST /rpc/profile/:profile/provider/:provider_id/:chain
```
Expand Down
Loading