diff --git a/CLAUDE.md b/CLAUDE.md index ab949ce7..d561c483 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -886,7 +886,7 @@ Namespace: openclaw- OpenClaw Pod ──HTTP:9000──> remote-signer Pod (signer.py skill) /data/keystores/.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). diff --git a/internal/embed/infrastructure/helmfile.yaml b/internal/embed/infrastructure/helmfile.yaml index 397ba067..12994f86 100644 --- a/internal/embed/infrastructure/helmfile.yaml +++ b/internal/embed/infrastructure/helmfile.yaml @@ -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 @@ -149,6 +175,8 @@ releases: name: erpc namespace: erpc spec: + hostnames: + - rpc.obol.stack parentRefs: - name: traefik-gateway namespace: traefik @@ -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 diff --git a/internal/embed/infrastructure/values/erpc-metadata.yaml.gotmpl b/internal/embed/infrastructure/values/erpc-metadata.yaml.gotmpl index 6b4ee111..463cf966 100644 --- a/internal/embed/infrastructure/values/erpc-metadata.yaml.gotmpl +++ b/internal/embed/infrastructure/values/erpc-metadata.yaml.gotmpl @@ -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 }}" } } } diff --git a/internal/embed/infrastructure/values/erpc.yaml.gotmpl b/internal/embed/infrastructure/values/erpc.yaml.gotmpl index 58b0f166..4173c4c2 100644 --- a/internal/embed/infrastructure/values/erpc.yaml.gotmpl +++ b/internal/embed/infrastructure/values/erpc.yaml.gotmpl @@ -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 @@ -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 diff --git a/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl b/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl index 22e52c73..7c7ae803 100644 --- a/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl +++ b/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl @@ -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 diff --git a/internal/embed/networks/aztec/helmfile.yaml.gotmpl b/internal/embed/networks/aztec/helmfile.yaml.gotmpl index 666ad283..5de915d0 100644 --- a/internal/embed/networks/aztec/helmfile.yaml.gotmpl +++ b/internal/embed/networks/aztec/helmfile.yaml.gotmpl @@ -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: diff --git a/internal/embed/networks/aztec/values.yaml.gotmpl b/internal/embed/networks/aztec/values.yaml.gotmpl index 59bde140..dbd56840 100644 --- a/internal/embed/networks/aztec/values.yaml.gotmpl +++ b/internal/embed/networks/aztec/values.yaml.gotmpl @@ -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 diff --git a/internal/embed/skills/addresses/SKILL.md b/internal/embed/skills/addresses/SKILL.md index 7e1eaf7d..5bd1bed9 100644 --- a/internal/embed/skills/addresses/SKILL.md +++ b/internal/embed/skills/addresses/SKILL.md @@ -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 ``` diff --git a/internal/embed/skills/ethereum-local-wallet/SKILL.md b/internal/embed/skills/ethereum-local-wallet/SKILL.md index b4c73e83..870cb5b0 100644 --- a/internal/embed/skills/ethereum-local-wallet/SKILL.md +++ b/internal/embed/skills/ethereum-local-wallet/SKILL.md @@ -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 diff --git a/internal/embed/skills/ethereum-local-wallet/scripts/signer.py b/internal/embed/skills/ethereum-local-wallet/scripts/signer.py index 3eca0a82..32194c84 100644 --- a/internal/embed/skills/ethereum-local-wallet/scripts/signer.py +++ b/internal/embed/skills/ethereum-local-wallet/scripts/signer.py @@ -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 @@ -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. diff --git a/internal/embed/skills/ethereum-networks/SKILL.md b/internal/embed/skills/ethereum-networks/SKILL.md index edcab1cd..124c2b66 100644 --- a/internal/embed/skills/ethereum-networks/SKILL.md +++ b/internal/embed/skills/ethereum-networks/SKILL.md @@ -27,7 +27,7 @@ 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). @@ -35,7 +35,7 @@ http://erpc.erpc.svc.cluster.local:4000/rpc/{network} 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) @@ -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 diff --git a/internal/embed/skills/ethereum-networks/scripts/rpc.py b/internal/embed/skills/ethereum-networks/scripts/rpc.py index a8c03e6d..155f3ec9 100644 --- a/internal/embed/skills/ethereum-networks/scripts/rpc.py +++ b/internal/embed/skills/ethereum-networks/scripts/rpc.py @@ -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 diff --git a/internal/embed/skills/ethereum-networks/scripts/rpc.sh b/internal/embed/skills/ethereum-networks/scripts/rpc.sh index 98394ed7..c4405a9d 100644 --- a/internal/embed/skills/ethereum-networks/scripts/rpc.sh +++ b/internal/embed/skills/ethereum-networks/scripts/rpc.sh @@ -5,11 +5,11 @@ # Usage: sh scripts/rpc.sh [--network ] [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 diff --git a/internal/embed/skills/frontend-playbook/SKILL.md b/internal/embed/skills/frontend-playbook/SKILL.md index 00f89508..a9b5e4c1 100644 --- a/internal/embed/skills/frontend-playbook/SKILL.md +++ b/internal/embed/skills/frontend-playbook/SKILL.md @@ -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) diff --git a/internal/embed/skills/gas/SKILL.md b/internal/embed/skills/gas/SKILL.md index b5fbceda..727f5cdb 100644 --- a/internal/embed/skills/gas/SKILL.md +++ b/internal/embed/skills/gas/SKILL.md @@ -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. @@ -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. diff --git a/internal/embed/skills/testing/SKILL.md b/internal/embed/skills/testing/SKILL.md index 46bc92e9..1e4cbfe2 100644 --- a/internal/embed/skills/testing/SKILL.md +++ b/internal/embed/skills/testing/SKILL.md @@ -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] diff --git a/internal/embed/skills/tools/SKILL.md b/internal/embed/skills/tools/SKILL.md index f2d58df0..5632c05d 100644 --- a/internal/embed/skills/tools/SKILL.md +++ b/internal/embed/skills/tools/SKILL.md @@ -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 @@ -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 @@ -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 ``` diff --git a/internal/openclaw/openclaw.go b/internal/openclaw/openclaw.go index b8beace9..dddbdb12 100644 --- a/internal/openclaw/openclaw.go +++ b/internal/openclaw/openclaw.go @@ -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. diff --git a/obolup.sh b/obolup.sh index 6ad8cdb1..62b29519 100755 --- a/obolup.sh +++ b/obolup.sh @@ -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 @@ -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