We need to collect the following information from the grafana dashboard for the offending withdrawal (as specified in the runbooks for this page).
event_tx_hash=0xe276fb2dea106d7cfeb9ed980101d7c64f5c7f75dafd557bcb6c4c322de280df
withdrawal_hash=0xadea6fbd204cce41eb72e0962a8bd9fc1bd02ffd04fc873e14353a12a78bbda7
proof_submitter=0xb47520aEA870C3fB9715C15fDF255C435F08c6c9
Set l1 and l2 below. Examples are l1=”sepolia” l2=”ink”
echo "INITIAL SETUP"
l1="mainnet"
l2="base"
if [ -z "$l1" ] || [ -z "$l2" ]; then
echo "ERROR: Variables l1 and/or l2 are not set. Please specify the L1 and L2 chains."
else
echo "Using L1: $l1, L2: $l2"
l2_rpc_url="<https://$l2-opn-geth-a-rpc-0-op-geth.primary.$l1.prod.oplabs.cloud>"
l2_rpc_url_node="<https://$l2-opn-geth-a-rpc-0-op-node.primary.$l1.prod.oplabs.cloud>"
l1_rpc_url="<https://proxyd-l1-consensus.primary.$l1.prod.oplabs.cloud>"
superchain_file_url="<https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/superchain/configs/$l1/$l2.toml>"
superchain_file_content=$(curl -s -L $superchain_file_url)
OPTIMISM_PORTAL_PROXY=$(echo "$superchain_file_content" | sed -n 's/.*OptimismPortalProxy = "\\([^"]*\\)".*/\\1/p')
L2ToL1MessagePasserAddress="0x4200000000000000000000000000000000000016"
echo "l2_rpc_url=$l2_rpc_url"
echo "l2_rpc_url_node=$l2_rpc_url_node"
echo "l1_rpc_url=$l1_rpc_url"
echo "OPTIMISM_PORTAL_PROXY=$OPTIMISM_PORTAL_PROXY"
echo "L2ToL1MessagePasserAddress=$L2ToL1MessagePasserAddress"
check_rpc() {
url=$1
response=$(curl -s -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' "$url")
if echo "$response" | grep -q '"result"'; then
echo "✅ $url is reachable and responded with chainId."
else
echo "❌ $url is NOT reachable or didn't return a valid response."
fi
}
echo "Checking RPC endpoints..."
check_rpc "$l2_rpc_url"
check_rpc "$l2_rpc_url_node"
check_rpc "$l1_rpc_url"
fi
<aside> ⚠️
It may happen the the l2_rpc_url or l2_rpc_url_node generated do not work, because for example they do not follow the name convention. In that case you will need to find the nodes url manually. If for example the name is not correct you may need to look directly into the code. For example for base-mainnet you could look at https://github.com/ethereum-optimism/k8s/blob/935d1f78d8ae3ed41356825d8ba1bae8e78d4de6/kustomize/automated-security-monitoring/oplabs-tools-security/faultproof-withdrawals/overlays/base-mainnet/faultproof-withdrawals.env#L2
and you would see that the nodes used are:
l2_rpc_url_node=https://base-opn-geth-a-rpc-2-op-node.tertiary.mainnet.prod.oplabs.cloud l2_rpc_url=https://base-opn-geth-a-rpc-2-op-geth.tertiary.mainnet.prod.oplabs.cloud
so set those instead manually in the shall
</aside>
l2_rpc_url_node=https://base-opn-geth-a-rpc-2-op-node.tertiary.mainnet.prod.oplabs.cloud
l2_rpc_url=https://base-opn-geth-a-rpc-2-op-geth.tertiary.mainnet.prod.oplabs.cloud
Get the dispute game, setting the withdrawal_hash below
event_signature="WithdrawalProvenExtension1(bytes32,address)"
event_topic=$(cast keccak "$event_signature")
receipt=$(cast receipt $event_tx_hash --rpc-url=$l1_rpc_url --json)
logs=$(echo "$receipt" | jq --arg topic "$event_topic" '.logs[] | select(.topics[0] == $topic)')
if [ -z "$logs" ]; then
echo "Error: No WithdrawalProvenExtension1 events found"
else
withdrawal_hash_from_topic=$(echo "$logs" | jq -r '.topics[1]')
proof_submitter_from_topic=$(echo "$logs" | jq -r '.topics[2]')
output=$(cast call $OPTIMISM_PORTAL_PROXY "provenWithdrawals(bytes32,address)(address,uint64)" $withdrawal_hash $proof_submitter --rpc-url $l1_rpc_url)
read game_proxy_address timestamp <<< "$output"
echo "Withdrawal Hash: $withdrawal_hash_from_topic"
echo "Proof Submitter: $proof_submitter_from_topic"
echo "Game Proxy Address: $game_proxy_address"
l2_block_number=$(cast call $game_proxy_address "l2BlockNumber()(uint256)" --rpc-url $l1_rpc_url | cut -d' ' -f1)
l2_block_number_hex=$(printf "0x%X" "$l2_block_number")
echo "L2 Block Number: $l2_block_number"
echo "L2 Block Number (Hex): $l2_block_number_hex"
root_claim=$(cast call $game_proxy_address "rootClaim()(bytes32)" --rpc-url $l1_rpc_url)
echo "Root Claim: $root_claim"
sent_message=$(cast call $L2ToL1MessagePasserAddress "sentMessages(bytes32)(bytes32)" $withdrawal_hash_from_topic --rpc-url $l2_rpc_url)
if [[ "$sent_message" == "0x0000000000000000000000000000000000000000000000000000000000000001" ]]; then
echo "Withdrawal is verified on L2 $l2_rpc_url ✅"
else
echo "Withdrawal is NOT verified on L2 $l2_rpc_url ❌"
** fi
l2_output_root=$(cast rpc optimism_outputAtBlock "$l2_block_number_hex" --rpc-url $l2_rpc_url_node | jq -r .outputRoot)
if [[ "$l2_output_root" == "$root_claim" ]]; then
echo "$l2_output_root == $root_claim Game Claim is good ✅"
else
echo "$l2_output_root != $root_claim Game Claim is NOT good ❌"
fi
fi