Quick Start: Run an ESC RPC Node
This gives you a local EVM-compatible RPC endpoint for Elastos Smart Contract chain (Chain ID 20). Use this for dApp development, running your own block explorer, or reducing dependency on public RPCs.
Initialize ESC
ESC does not depend on ELA for RPC-only operation. You only need the ESC binary itself.
~/node/node.sh esc init
This downloads the ESC binary (a modified geth), creates a keystore, and saves the keystore password to ~/.config/elastos/esc.txt.
Force Light-Serve Mode
~/node/node.sh esc init creates a keystore password file at ~/.config/elastos/esc.txt. When that file exists, ~/node/node.sh esc start launches ESC in mining mode, which requires ELA's keystore for PBFT consensus. Since an RPC node does not participate in consensus, remove the password file to force light-serve mode:
rm ~/.config/elastos/esc.txt
Start ESC
~/node/node.sh esc start
Without esc.txt, ESC starts in light-serve mode. The command node.sh actually runs is:
./esc --datadir data --lightserv 10 \
--rpc --rpcaddr "$(evm_rpc_bind)" --rpcapi 'eth,net,web3,txpool' --rpcvhosts '*' \
--ws --wsaddr "$(evm_rpc_bind)" --wsorigins '*'
It serves HTTP JSON-RPC on 127.0.0.1:20636. The exposed API surface is eth,net,web3,txpool — node.sh never enables admin, db, or personal. The --ws flag is passed, but light-serve does not bind a fixed WebSocket port the way the HTTP RPC does, so treat 20636 HTTP JSON-RPC as the stable endpoint. (Port 20632 is the ESC Oracle RPC, a separate cross-chain service — it is not this node's WebSocket listener.)
$(evm_rpc_bind) resolves to 127.0.0.1 unless you change it, so out of the box the RPC is reachable only from the server itself. Change the bind address persistently:
echo '0.0.0.0' > ~/.config/elastos/evm_rpc_bind # or: export EVM_RPC_BIND=0.0.0.0
~/node/node.sh esc restart
An invalid value fails closed to 127.0.0.1. For anything internet-facing, do not bind 0.0.0.0 directly — keep it on 127.0.0.1 and front it with the reverse proxy below, which is where you add TLS, authentication, and rate limiting.
Validators run ESC in mining mode, which uses ELA's keystore for PBFT cross-chain signing. An RPC node only serves queries, so ELA is not needed.
Like upstream geth, this binary keeps only the recent state trie (~128 blocks) by default. Serving eth_getBalance / eth_call at old block heights requires running ESC in archive mode — budget roughly 2–3× the storage. This is a custom configuration beyond node.sh's managed default; size your disk for it before you sync. Check current usage with du -sh ~/node/esc/data.
Connect
With the default 127.0.0.1 bind, reach the node from the server itself or over an SSH tunnel; for anything else, connect through the reverse proxy below (the production endpoint).
Local / tunnel: http://127.0.0.1:20636
Production: https://rpc.yourdomain.com
Chain ID: 20
// ethers.js — production endpoint (TLS + auth terminated at the proxy)
const provider = new ethers.JsonRpcProvider('https://rpc.yourdomain.com');
const blockNumber = await provider.getBlockNumber();
Expose it for production
ESC and EID are geth-based and have no native RPC authentication — unlike ELA, which has RPC User/Pass and a WhiteIPList. An internet-facing endpoint must therefore be gated by the fronting proxy. Keep the node on 127.0.0.1 and terminate TLS, authentication, and rate limiting in nginx:
# In the http {} context: define the rate-limit zone and an API-key allowlist.
limit_req_zone $binary_remote_addr zone=rpc:10m rate=20r/s;
map $http_x_api_key $api_ok {
default 0;
"ISSUE_A_KEY" 1; # one key per consumer; rotate by editing this map
}
server {
listen 443 ssl;
server_name rpc.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/rpc.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rpc.yourdomain.com/privkey.pem;
location / {
if ($api_ok = 0) { return 401; } # ESC/EID have no native auth — enforce it here
limit_req zone=rpc burst=50 nodelay;
proxy_pass http://127.0.0.1:20636;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}