#!/bin/bash SEPOLIA_RPC=( "https://sepolia.drpc.org" "https://sepolia.gateway.tenderly.co" "https://api.zan.top/eth-sepolia" "https://rpc.sepolia.ethpandaops.io" "https://ethereum-sepolia-rpc.publicnode.com" "https://1rpc.io/sepolia" "https://0xrpc.io/sep" "https://eth-sepolia.api.onfinality.io/public" ) MORDOR_RPC=( "https://0xrpc.io/mordor" "https://geth-mordor.etc-network.info" "https://rpc.mordor.etccooperative.org" ) HOODI_RPC=( "https://rpc.hoodi.ethpandaops.io" "https://0xrpc.io/hoodi" "https://ethereum-hoodi.gateway.tatum.io" "https://rpc.sentio.xyz/hoodi" ) HOODI_GATEKEEPER="0xc9D5f50e17eeEAF39E0D0cDbbcF4B04FA4A4Bbd6" show_help() { cat << EOF Usage: $(basename "$0") [OPTIONS] Options: --chain Specify network: 'sepolia', 'hoodi' or 'mordor'. If omitted, checks all. --output Base directory to save results. Creates a subfolder with Unix Timestamp. --rpcs Comma-separated list of custom RPC endpoints. Disables default networks lists. --help Show this help message. Example: $0 --chain sepolia --output ./logs EOF exit 0 } spinner() { local pid=$1 local net_name=$2 local delay=0.1 local spinstr='|/-\' tput civis while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do local temp=${spinstr#?} printf "\r [%c] Checking %s... " "$spinstr" "$net_name" local spinstr=$temp${spinstr%"$temp"} sleep $delay done printf " \r" tput cnorm } pad32() { local h="$1" h="${h#0x}" printf "%064s" "$h" | tr ' ' '0' } build_calldata() { local selector="$1" local at_block="$2" local s_hex local b_hex s_hex=$(printf "%x" 0) b_hex=$(printf "%x" "$at_block") echo "0x${selector#0x}$(pad32 "$s_hex")$(pad32 "$b_hex")" } decode_get_root() { local res_json="$1" local hex hex=$(echo "$res_json" | grep -o '"result":"[^"]*' | cut -d'"' -f4) if [[ -z "$hex" || "$hex" == "null" ]]; then echo "ERROR" return fi hex="${hex#0x}" if (( ${#hex} < 128 )); then echo "ERROR" return fi local word1="${hex:0:64}" local word2="${hex:64:64}" local num num=$(printf "%d" "0x$word2" 2>/dev/null) [[ -z "$num" ]] && num="$word2" echo "0x${word1} ${num}" } check_network() { local name=$1 local gatekeeper=$2 shift 2 local rpcs=("$@") local target_dir="${OUT_DIR:-/tmp}" local tmp_file="${target_dir}/$(date +%s)_${name,,}" local root_width=66 local sess_width=6 local max_len=0 for e in "${rpcs[@]}"; do (( ${#e} > max_len )) && max_len=${#e}; done [[ $max_len -lt 20 ]] && max_len=20 local total_width=$(( max_len + 95 )) local line=$(printf "%.0s-" $(seq 1 $total_width)) local header_fmt="| %-${max_len}s | %-10s | %-${root_width}s | %-${sess_width}s |\n" ( for endpoint in "${rpcs[@]}"; do res_b=$(curl -s -X POST -H "Content-Type: application/json" --max-time 10 \ --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' "$endpoint") hex=$(echo "$res_b" | grep -o '"result":"[^"]*' | cut -d'"' -f4) [[ -n "$hex" && "$hex" != "null" ]] && blk=$(printf "%d" "$hex") || blk="ERROR" root="N/A" if [[ -n "$blk" && "$blk" != "ERROR" ]]; then calldata=$(build_calldata "$SELECTOR" "$blk") payload=$(printf '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"%s","data":"%s"},"latest"],"id":1}' "$gatekeeper" "$calldata") res_c=$(curl -s -X POST -H "Content-Type: application/json" --max-time 10 --data "$payload" "$endpoint") root=$(decode_get_root "$res_c") fi echo "$endpoint|$blk|$root" done ) > "$tmp_file" & local worker_pid=$! if [[ -z "$OUT_DIR" ]]; then spinner $worker_pid "$name" local table_output="$line\n" table_output+=$(printf "$header_fmt" "Endpoint" "Block" "Merkle Root" "Session")"\n" table_output+="$line\n" while IFS="|" read -r url blk root; do if [[ "$root" == *" "* ]]; then hash="${root%% *}" sess="${root##* }" else hash="$root" sess="-" fi table_output+=$(printf "$header_fmt" "$url" "$blk" "$hash" "$sess")"\n" done < "$tmp_file" table_output+="$line" echo -e "$table_output" rm "$tmp_file" else wait $worker_pid fi } CHOSEN_CHAIN="" BASE_OUT_DIR="" CUSTOM_RPCS="" SELECTOR="d4be7935" while [[ $# -gt 0 ]]; do case "$1" in --help) show_help ;; --chain) CHOSEN_CHAIN="$2"; shift 2 ;; --output) BASE_OUT_DIR="$2"; shift 2 ;; --rpcs) CUSTOM_RPCS="$2"; shift 2 ;; *) echo "Unknown option: $1. Use --help for usage."; exit 1 ;; esac done if [[ -n "$BASE_OUT_DIR" ]]; then OUT_DIR="${BASE_OUT_DIR}" mkdir -p "$OUT_DIR" fi if [[ -n "$CUSTOM_RPCS" ]]; then IFS=',' read -r -a USER_RPC_ARRAY <<< "$CUSTOM_RPCS" check_network "Custom" "${USER_RPC_ARRAY[@]}" else if [ "$CHOSEN_CHAIN" == "sepolia" ]; then check_network "Sepolia" "$SEPOLIA_GATEKEEPER" "${HOODI_RPC[@]}" elif [ "$CHOSEN_CHAIN" == "mordor" ]; then check_network "Mordor" "$MORDOR_GATEKEEPER" "${HOODI_RPC[@]}" elif [ "$CHOSEN_CHAIN" == "hoodi" ]; then check_network "Hoodi" "$HOODI_GATEKEEPER" "${HOODI_RPC[@]}" else check_network "Sepolia" "$SEPOLIA_GATEKEEPER" "${SEPOLIA_RPC[@]}" check_network "Mordor" "$MORDOR_GATEKEEPER" "${MORDOR_RPC[@]}" check_network "Hoodi" "$HOODI_GATEKEEPER" "${HOODI_RPC[@]}" fi fi