Skip to content
Open
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 CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ Namespace: openclaw-<id>
OpenClaw Pod ──HTTP:9000──> remote-signer Pod
(signer.py skill) /data/keystores/<uuid>.json (V3)
└── eth_sendRawTransaction ──> eRPC (:4000/rpc)
└── eth_sendRawTransaction ──> eRPC (rpc.erpc.svc.cluster.local)
```

**Key generation**: secp256k1 via `crypto/rand` + `github.com/decred/dcrd/dcrec/secp256k1/v4`, encrypted to Web3 Secret Storage V3 format (scrypt + AES-128-CTR).
Expand Down
34 changes: 31 additions & 3 deletions internal/embed/infrastructure/helmfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,32 @@ releases:
values:
- ./values/erpc.yaml.gotmpl

# eRPC port-80 alias Service — lets in-cluster consumers reach eRPC
# without specifying :4000 (e.g. http://erpc.erpc.svc.cluster.local/mainnet).
- name: erpc-http
namespace: erpc
chart: bedag/raw
version: 2.0.2
needs:
- erpc/erpc
values:
- resources:
- apiVersion: v1
kind: Service
metadata:
name: rpc
namespace: erpc
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: erpc
app.kubernetes.io/instance: erpc
ports:
- port: 80
targetPort: 4000
protocol: TCP
name: http

# eRPC HTTPRoute
- name: erpc-httproute
namespace: erpc
Expand All @@ -149,6 +175,8 @@ releases:
name: erpc
namespace: erpc
spec:
hostnames:
- rpc.obol.stack
parentRefs:
- name: traefik-gateway
namespace: traefik
Expand All @@ -157,10 +185,10 @@ releases:
- matches:
- path:
type: PathPrefix
value: /rpc
value: /
backendRefs:
- name: erpc
port: 4000
- name: rpc
port: 80

# eRPC metadata ConfigMap for frontend discovery
- name: erpc-metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ resources:
"network": "{{ .Values.network }}",
"endpoints": {
"rpc": {
"external": "http://obol.stack/rpc/{{ .Values.network }}",
"internal": "http://erpc.erpc.svc.cluster.local:4000/rpc/{{ .Values.network }}"
"external": "http://rpc.obol.stack/{{ .Values.network }}",
"internal": "http://rpc.erpc.svc.cluster.local/{{ .Values.network }}"
}
}
}
39 changes: 28 additions & 11 deletions internal/embed/infrastructure/values/erpc.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,16 @@ config: |-
port: 4001

projects:
- id: rpc
- id: mainnet
upstreams:
- id: obol-rpc-mainnet
endpoint: https://{{ $erpcGcpAuth }}@erpc.gcp.obol.tech/mainnet/evm/1
evm:
chainId: 1
- id: obol-rpc-hoodi
endpoint: https://{{ $erpcGcpAuth }}@erpc.gcp.obol.tech/hoodi/evm/560048
evm:
chainId: 560048
- id: allnodes-rpc-hoodi
endpoint: https://ethereum-hoodi-rpc.publicnode.com
evm:
chainId: 560048
networks:
- architecture: evm
evm:
chainId: 1
alias: mainnet
selectionPolicy:
evalInterval: 1m
evalPerMethod: true
Expand All @@ -84,10 +75,36 @@ config: |-
hedge:
delay: 500ms
maxCount: 1
cors:
allowedOrigins:
- "*"
allowedMethods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowedHeaders:
- "*"
exposedHeaders:
- "X-Request-ID"
allowCredentials: true
maxAge: 3600

- id: hoodi
upstreams:
- id: obol-rpc-hoodi
endpoint: https://{{ $erpcGcpAuth }}@erpc.gcp.obol.tech/hoodi/evm/560048
evm:
chainId: 560048
- id: allnodes-rpc-hoodi
endpoint: https://ethereum-hoodi-rpc.publicnode.com
evm:
chainId: 560048
networks:
- architecture: evm
evm:
chainId: 560048
alias: hoodi
failsafe:
timeout:
duration: 30s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ serviceAccount:
image:
environment:
- name: NEXT_PUBLIC_ERPC_URL
value: "https://{{ $publicDomain }}/rpc"
value: "http://rpc.{{ $publicDomain }}"
- name: NEXT_PUBLIC_AZTEC_SEQUENCER_URL
value: "http://l2-sequencer-node-mainnet-node.aztec.svc.cluster.local:8080"
- name: BETTER_AUTH_SECRET
Expand Down
2 changes: 1 addition & 1 deletion internal/embed/networks/aztec/helmfile.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ releases:
- --network
- '{{ .Values.network }}'
l1ExecutionUrls:
- '{{ if .Values.l1ExecutionUrl }}{{ .Values.l1ExecutionUrl }}{{ else }}http://erpc.erpc.svc.cluster.local:4000/rpc/{{ .Values.network }}{{ end }}'
- '{{ if .Values.l1ExecutionUrl }}{{ .Values.l1ExecutionUrl }}{{ else }}http://rpc.erpc.svc.cluster.local/{{ .Values.network }}{{ end }}'
l1ConsensusUrls:
- '{{ .Values.l1ConsensusUrl }}'
resources:
Expand Down
2 changes: 1 addition & 1 deletion internal/embed/networks/aztec/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ network: {{.Network}}
attesterPrivateKey: {{.AttesterPrivateKey}}

# @default ""
# @description L1 Execution RPC URL (defaults to ERPC: http://erpc.erpc.svc.cluster.local:4000/rpc/{network})
# @description L1 Execution RPC URL (defaults to ERPC: http://rpc.erpc.svc.cluster.local/{network})
l1ExecutionUrl: {{.L1ExecutionUrl}}

# @default https://ethereum-beacon-api.publicnode.com
Expand Down
2 changes: 1 addition & 1 deletion internal/embed/skills/addresses/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ Full function reference and JSON ABIs for ERC-8004 registries coming soon via th

```bash
# Check bytecode exists (use local eRPC if running in Obol Stack)
cast code 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --rpc-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
cast code 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --rpc-url http://rpc.erpc.svc.cluster.local/mainnet
# Fallback public RPC: https://eth.llamarpc.com
```

Expand Down
2 changes: 1 addition & 1 deletion internal/embed/skills/ethereum-local-wallet/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Supported networks: `mainnet`, `hoodi`, `sepolia` (depends on eRPC configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `REMOTE_SIGNER_URL` | `http://remote-signer:9000` | Remote-signer REST API base URL |
| `ERPC_URL` | `http://erpc.erpc.svc.cluster.local:4000/rpc` | eRPC gateway for RPC calls |
| `ERPC_URL` | `http://rpc.erpc.svc.cluster.local` | eRPC gateway for RPC calls |
| `ERPC_NETWORK` | `mainnet` | Default network for RPC routing |

## Security Model
Expand Down
4 changes: 2 additions & 2 deletions internal/embed/skills/ethereum-local-wallet/scripts/signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Environment:
REMOTE_SIGNER_URL Base URL for remote-signer (default: http://remote-signer:9000)
ERPC_URL Base URL for eRPC gateway (default: http://erpc.erpc.svc.cluster.local:4000/rpc)
ERPC_URL Base URL for eRPC gateway (default: http://rpc.erpc.svc.cluster.local)
ERPC_NETWORK Default network (default: mainnet)
"""
import json
Expand All @@ -17,7 +17,7 @@
import urllib.error

SIGNER_URL = os.environ.get("REMOTE_SIGNER_URL", "http://remote-signer:9000")
ERPC_BASE = os.environ.get("ERPC_URL", "http://erpc.erpc.svc.cluster.local:4000/rpc")
ERPC_BASE = os.environ.get("ERPC_URL", "http://rpc.erpc.svc.cluster.local")
NETWORK = os.environ.get("ERPC_NETWORK", "mainnet")

# Chain IDs for known networks.
Expand Down
6 changes: 3 additions & 3 deletions internal/embed/skills/ethereum-networks/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ Query Ethereum blockchain data through the local eRPC gateway. Supports any JSON
The eRPC gateway routes to whichever Ethereum networks are installed:

```
http://erpc.erpc.svc.cluster.local:4000/rpc/{network}
http://rpc.erpc.svc.cluster.local/{network}
```

`mainnet` is always available. Other networks (e.g. `hoodi`) are available if installed. You can also use `evm/{chainId}` (e.g. `evm/560048` for Hoodi).

To see which networks are connected:

```bash
curl -s http://erpc.erpc.svc.cluster.local:4000/ | python3 -m json.tool
curl -s http://rpc.erpc.svc.cluster.local/ | python3 -m json.tool
```

## Quick Start (cast)
Expand Down Expand Up @@ -172,7 +172,7 @@ python3 scripts/rpc.py --network hoodi eth_chainId
## Constraints

- **Read-only** — no private keys, no signing, no state changes
- **Local routing** — always route through eRPC at `http://erpc.erpc.svc.cluster.local:4000/rpc/`, never call external RPC providers
- **Local routing** — always route through eRPC at `http://rpc.erpc.svc.cluster.local/`, never call external RPC providers
- **Shell is `sh`, not `bash`** — do not use bashisms like `${var//pattern}`, `${var:offset}`, `[[ ]]`, or arrays. Use POSIX-compatible syntax only
- **`cast` preferred** — use `rpc.sh` (Foundry cast) for all queries. Fall back to `rpc.py` (Python stdlib) only if cast is unavailable
- **Always check for null results** — RPC methods like `eth_getTransactionByHash` return `null` for unknown hashes
4 changes: 2 additions & 2 deletions internal/embed/skills/ethereum-networks/scripts/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import urllib.error
import urllib.request

# eRPC requires /rpc/{network} path. ERPC_URL is the base (without network).
ERPC_BASE = os.environ.get("ERPC_URL", "http://erpc.erpc.svc.cluster.local:4000/rpc")
# eRPC uses /{network} path (project ID = network name). ERPC_URL is the base.
ERPC_BASE = os.environ.get("ERPC_URL", "http://rpc.erpc.svc.cluster.local")
DEFAULT_NETWORK = os.environ.get("ERPC_NETWORK", "mainnet")

# Methods that take no params
Expand Down
4 changes: 2 additions & 2 deletions internal/embed/skills/ethereum-networks/scripts/rpc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
# Usage: sh scripts/rpc.sh [--network <name>] <command> [args...]
#
# Environment:
# ERPC_URL Base URL for eRPC gateway (default: http://erpc.erpc.svc.cluster.local:4000/rpc)
# ERPC_URL Base URL for eRPC gateway (default: http://rpc.erpc.svc.cluster.local)
# ERPC_NETWORK Default network (default: mainnet)
set -eu

ERPC_BASE="${ERPC_URL:-http://erpc.erpc.svc.cluster.local:4000/rpc}"
ERPC_BASE="${ERPC_URL:-http://rpc.erpc.svc.cluster.local}"
NETWORK="${ERPC_NETWORK:-mainnet}"

# Parse --network flag
Expand Down
2 changes: 1 addition & 1 deletion internal/embed/skills/frontend-playbook/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ For **updates** to an existing app: skip Tx 1, only do Tx 2.

```bash
# 1. Onchain content hash matches
ERPC="http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet" # or https://eth.llamarpc.com
ERPC="http://rpc.erpc.svc.cluster.local/mainnet" # or https://eth.llamarpc.com
RESOLVER=$(cast call 0x00000000000C2e074eC69A0dFb2997BA6C7d2e1e \
"resolver(bytes32)(address)" $(cast namehash myapp.yourname.eth) \
--rpc-url $ERPC)
Expand Down
10 changes: 5 additions & 5 deletions internal/embed/skills/gas/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ sh scripts/rpc.sh gas-price
sh scripts/rpc.sh base-fee

# Via cast directly
cast gas-price --rpc-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
cast base-fee --rpc-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
cast blob-basefee --rpc-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
cast gas-price --rpc-url http://rpc.erpc.svc.cluster.local/mainnet
cast base-fee --rpc-url http://rpc.erpc.svc.cluster.local/mainnet
cast blob-basefee --rpc-url http://rpc.erpc.svc.cluster.local/mainnet

# Estimate gas for a specific call
cast estimate 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
"transfer(address,uint256)" 0xRecipient 1000000 \
--rpc-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
--rpc-url http://rpc.erpc.svc.cluster.local/mainnet
```

Note: `scripts/rpc.sh` is from the `ethereum-networks` skill. Copy it or reference it directly.
Expand All @@ -127,7 +127,7 @@ Note: `scripts/rpc.sh` is from the `ethereum-networks` skill. Copy it or referen

If this date is more than 30 days old, verify current gas with:
```bash
cast base-fee --rpc-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
cast base-fee --rpc-url http://rpc.erpc.svc.cluster.local/mainnet
```

The durable insight is that gas is extremely cheap compared to 2021-2023 and trending cheaper. Specific numbers may drift but the order of magnitude is stable.
4 changes: 2 additions & 2 deletions internal/embed/skills/testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,10 @@ contract SwapTest is Test {

```bash
# Fork from local eRPC (if running in Obol Stack with mainnet installed)
forge test --fork-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
forge test --fork-url http://rpc.erpc.svc.cluster.local/mainnet

# Fork at specific block (reproducible)
forge test --fork-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet --fork-block-number 19000000
forge test --fork-url http://rpc.erpc.svc.cluster.local/mainnet --fork-block-number 19000000

# Set in foundry.toml to avoid CLI flags
# [rpc_endpoints]
Expand Down
8 changes: 4 additions & 4 deletions internal/embed/skills/tools/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const response = await x402Fetch('https://api.example.com/data', {
`cast` is pre-installed in the OpenClaw image. The local eRPC gateway is the default RPC:

```bash
RPC="http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet"
RPC="http://rpc.erpc.svc.cluster.local/mainnet"

# Read contract (with ABI decoding)
cast call 0xAddr "balanceOf(address)(uint256)" 0xWallet --rpc-url $RPC
Expand Down Expand Up @@ -126,8 +126,8 @@ For a full cast-based query tool, see the `ethereum-networks` skill (`scripts/rp
## RPC Providers

**Obol Stack (local, preferred):**
- `http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet` — local eRPC gateway, routes to installed networks
- Supports `/rpc/{network}` (mainnet, hoodi, sepolia) and `/rpc/evm/{chainId}` routing
- `http://rpc.erpc.svc.cluster.local/mainnet` — local eRPC gateway, routes to installed networks
- Supports `/{network}` (mainnet, hoodi, sepolia) and `/evm/{chainId}` routing

**Free (testing/fallback):**
- `https://eth.llamarpc.com` — LlamaNodes, no key
Expand Down Expand Up @@ -172,7 +172,7 @@ MCP servers are composable — agents can use multiple together.

**Fork mainnet locally:**
```bash
anvil --fork-url http://erpc.erpc.svc.cluster.local:4000/rpc/mainnet
anvil --fork-url http://rpc.erpc.svc.cluster.local/mainnet
# Now test against real contracts with fake ETH at http://localhost:8545
# Fallback public RPC: https://eth.llamarpc.com
```
Expand Down
2 changes: 1 addition & 1 deletion internal/openclaw/openclaw.go
Original file line number Diff line number Diff line change
Expand Up @@ -1310,7 +1310,7 @@ models:

b.WriteString(`# eRPC integration
erpc:
url: http://erpc.erpc.svc.cluster.local:4000/rpc
url: http://rpc.erpc.svc.cluster.local

# Remote-signer wallet for Ethereum transaction signing.
# The remote-signer runs in the same namespace as OpenClaw.
Expand Down
7 changes: 6 additions & 1 deletion obolup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,11 @@ check_hosts_file() {
if grep -q "obol.stack" /etc/hosts 2>/dev/null; then
# Check if it points to localhost (127.0.0.1 or ::1)
if grep -E "^(127\.0\.0\.1|::1)[[:space:]].*obol\.stack" /etc/hosts >/dev/null 2>&1; then
# Check if rpc.obol.stack is also present
if ! grep -q "rpc\.obol\.stack" /etc/hosts 2>/dev/null; then
log_info "obol.stack configured but rpc.obol.stack missing — will update"
return 1
fi
log_success "obol.stack already configured in /etc/hosts"
return 0
else
Expand All @@ -1143,7 +1148,7 @@ check_hosts_file() {
update_hosts_file() {
log_info "Adding obol.stack to /etc/hosts..."

local hosts_entry="127.0.0.1 obol.stack"
local hosts_entry="127.0.0.1 obol.stack rpc.obol.stack"

# Check if sudo is available
if ! command_exists sudo; then
Expand Down
Loading