#!/usr/bin/env bash # Cancel pending transactions by sending replacement transactions with higher gas # Usage: ./cancel-pending-transactions.sh [--force] # --force Send one replacement tx at current nonce (use when deploy fails with # "Replacement transaction underpriced" but script reports no pending) set -uo pipefail FORCE_REPLACE=false [[ "${1:-}" == "--force" ]] && FORCE_REPLACE=true SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # Prefer smom-dbis-138 in this repo; fallback to legacy path SOURCE_PROJECT="${PROJECT_ROOT}/smom-dbis-138" [ ! -f "$SOURCE_PROJECT/.env" ] && [ -d "/home/intlc/projects/smom-dbis-138" ] && SOURCE_PROJECT="/home/intlc/projects/smom-dbis-138" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Load environment variables if [ -f "$SOURCE_PROJECT/.env" ]; then source "$SOURCE_PROJECT/.env" else log_error ".env file not found in $SOURCE_PROJECT" exit 1 fi # Chain 138 RPC (VMID 2101); prefer RPC_URL_138 from .env RPC_URL="${RPC_URL_138:-http://192.168.11.211:8545}" if [ -z "${PRIVATE_KEY:-}" ]; then log_error "PRIVATE_KEY not set in .env file" exit 1 fi DEPLOYER=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || echo "") if [ -z "$DEPLOYER" ]; then log_error "Failed to get deployer address" exit 1 fi log_info "=========================================" log_info "Cancel Pending Transactions" log_info "=========================================" log_info "" log_info "Deployer: $DEPLOYER" log_info "RPC URL: $RPC_URL" log_info "" # Get current and pending nonces (curl works regardless of cast version) LATEST_HEX=$(curl -s -X POST -H "Content-Type: application/json" \ -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$DEPLOYER\",\"latest\"],\"id\":1}" \ "$RPC_URL" 2>/dev/null | jq -r '.result // "0x0"') PENDING_HEX=$(curl -s -X POST -H "Content-Type: application/json" \ -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$DEPLOYER\",\"pending\"],\"id\":1}" \ "$RPC_URL" 2>/dev/null | jq -r '.result // "0x0"') CURRENT_NONCE=$(printf '%d' "${LATEST_HEX:-0x0}") PENDING_NONCE=$(printf '%d' "${PENDING_HEX:-0x0}") log_info "Current nonce: $CURRENT_NONCE" log_info "Pending nonce: $PENDING_NONCE" if [ "$PENDING_NONCE" -le "$CURRENT_NONCE" ]; then if [[ "$FORCE_REPLACE" == "true" ]]; then log_warn "Forcing one replacement tx at nonce $CURRENT_NONCE (in case of stuck tx not reported as pending)" TX_OUTPUT=$(cast send "$DEPLOYER" \ --value 0 \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --gas-price "${GAS_PRICE_138:-500000000000}" \ --gas-limit 21000 \ --nonce "$CURRENT_NONCE" \ --legacy \ 2>&1 || echo "FAILED") if echo "$TX_OUTPUT" | grep -qE "transactionHash|Success"; then log_success "✓ Replacement sent; wait a few seconds then retry deploy" else log_error "Replace failed: $TX_OUTPUT" exit 1 fi exit 0 fi log_success "✓ No pending transactions found" log_info "All transactions have been mined" exit 0 fi PENDING_COUNT=$((PENDING_NONCE - CURRENT_NONCE)) log_warn "Found $PENDING_COUNT pending transaction(s)" log_info "" # Cancel pending transactions by sending replacement transactions log_info "Canceling pending transactions..." log_info "Sending replacement transactions with high gas price to cancel pending ones" log_info "" CANCELED=0 for ((nonce = CURRENT_NONCE; nonce < PENDING_NONCE; nonce++)); do log_info "Canceling transaction with nonce $nonce..." # Send a transaction to self with 0 value and high gas price (replaces stuck tx at this nonce) # Use --legacy for Besu/Chain 138; 200 gwei so replacement is accepted TX_OUTPUT=$(cast send "$DEPLOYER" \ --value 0 \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --gas-price 200000000000 \ --gas-limit 21000 \ --nonce "$nonce" \ --legacy \ 2>&1 || echo "FAILED") if echo "$TX_OUTPUT" | grep -qE "transactionHash|Success"; then HASH=$(echo "$TX_OUTPUT" | grep -oE "transactionHash[[:space:]]+0x[0-9a-fA-F]{64}" | awk '{print $2}' || echo "") log_success "✓ Canceled transaction (nonce $nonce): $HASH" ((CANCELED++)) sleep 2 else ERR=$(echo "$TX_OUTPUT" | grep -E "Error|reverted" | head -1 || echo "Unknown") log_warn "⚠ Could not cancel transaction (nonce $nonce): $ERR" fi done log_info "" log_success "=========================================" log_success "Transaction Cancellation Complete!" log_success "=========================================" log_info "" log_info "Summary:" log_info " Pending transactions found: $PENDING_COUNT" log_info " Successfully canceled: $CANCELED" log_info "" log_info "Waiting 10 seconds for transactions to be mined..." sleep 10 # Check final status FINAL_HEX=$(curl -s -X POST -H "Content-Type: application/json" \ -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$DEPLOYER\",\"latest\"],\"id\":1}" \ "$RPC_URL" 2>/dev/null | jq -r '.result // "0x0"') FINAL_NONCE=$(printf '%d' "${FINAL_HEX:-0x0}") log_info "Final nonce: $FINAL_NONCE" if [ "$FINAL_NONCE" -ge "$PENDING_NONCE" ]; then log_success "✓ All pending transactions have been processed" else log_warn "⚠ Some transactions may still be pending" log_info "Wait a bit longer and check again" fi log_info ""