155 lines
4.9 KiB
Bash
Executable File
155 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Send Transaction with Optimal Gas Pricing
|
|
# Prevents stuck transactions by using dynamic gas pricing
|
|
# Usage: ./send-with-optimal-gas.sh <contract> <function> [args...]
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
# 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 "$PROJECT_ROOT/.env" ]; then
|
|
source "$PROJECT_ROOT/.env"
|
|
elif [ -f "$PROJECT_ROOT/../.env" ]; then
|
|
source "$PROJECT_ROOT/../.env"
|
|
fi
|
|
|
|
# Configuration
|
|
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
|
|
GAS_MULTIPLIER="${GAS_MULTIPLIER:-1.5}" # Use 1.5x current gas for faster inclusion
|
|
|
|
if [ -z "${PRIVATE_KEY:-}" ]; then
|
|
log_error "PRIVATE_KEY not found in .env"
|
|
exit 1
|
|
fi
|
|
|
|
# Get optimal gas price (prefer Etherscan API, fallback to RPC)
|
|
get_optimal_gas() {
|
|
# Try Etherscan API first (if available)
|
|
if [ -f "$SCRIPT_DIR/get-optimal-gas-from-api.sh" ]; then
|
|
API_GAS=$("$SCRIPT_DIR/get-optimal-gas-from-api.sh" "proposed" 2>/dev/null || echo "")
|
|
if [ -n "$API_GAS" ] && [ "$API_GAS" != "0" ]; then
|
|
# Use API gas with multiplier for faster inclusion
|
|
local optimal_gas=$(echo "scale=0; $API_GAS * $GAS_MULTIPLIER / 1" | bc 2>/dev/null || echo "$API_GAS")
|
|
echo "$optimal_gas"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback to RPC gas price
|
|
local current_gas=$(cast gas-price --rpc-url "$RPC_URL" 2>/dev/null || echo "1000000000")
|
|
local optimal_gas=$(echo "scale=0; $current_gas * $GAS_MULTIPLIER / 1" | bc 2>/dev/null || echo "$current_gas")
|
|
echo "$optimal_gas"
|
|
}
|
|
|
|
# Check nonce
|
|
check_nonce() {
|
|
local account="$1"
|
|
local current_nonce=$(cast nonce "$account" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
|
|
local pending_nonce=$(cast nonce "$account" --rpc-url "$RPC_URL" --pending 2>/dev/null || echo "$current_nonce")
|
|
|
|
if [ "$pending_nonce" -gt "$current_nonce" ]; then
|
|
local pending_count=$((pending_nonce - current_nonce))
|
|
log_warn "Found $pending_count pending transaction(s)"
|
|
log_info "Current nonce: $current_nonce, Pending nonce: $pending_nonce"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Get account address
|
|
ACCOUNT=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || echo "")
|
|
if [ -z "$ACCOUNT" ]; then
|
|
log_error "Could not derive address from PRIVATE_KEY"
|
|
exit 1
|
|
fi
|
|
|
|
# Check nonce before sending
|
|
log_info "Checking nonce..."
|
|
if ! check_nonce "$ACCOUNT"; then
|
|
log_warn "Pending transactions detected"
|
|
log_info "Waiting 10 seconds for pending transactions..."
|
|
sleep 10
|
|
|
|
# Re-check
|
|
if ! check_nonce "$ACCOUNT"; then
|
|
log_error "Still have pending transactions"
|
|
log_info "Consider waiting longer or using higher gas price to replace"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Get optimal gas price
|
|
CURRENT_GAS=$(cast gas-price --rpc-url "$RPC_URL" 2>/dev/null || echo "1000000000")
|
|
OPTIMAL_GAS=$(get_optimal_gas)
|
|
|
|
CURRENT_GAS_GWEI=$(echo "scale=2; $CURRENT_GAS / 1000000000" | bc 2>/dev/null || echo "1")
|
|
OPTIMAL_GAS_GWEI=$(echo "scale=2; $OPTIMAL_GAS / 1000000000" | bc 2>/dev/null || echo "1.5")
|
|
|
|
log_info "Current gas price: $CURRENT_GAS_GWEI gwei"
|
|
log_info "Optimal gas price: $OPTIMAL_GAS_GWEI gwei (${GAS_MULTIPLIER}x multiplier)"
|
|
log_info ""
|
|
|
|
# Get current nonce
|
|
CURRENT_NONCE=$(cast nonce "$ACCOUNT" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
|
|
log_info "Using nonce: $CURRENT_NONCE"
|
|
log_info ""
|
|
|
|
# Build cast send command
|
|
CONTRACT="$1"
|
|
FUNCTION="$2"
|
|
shift 2
|
|
ARGS="$@"
|
|
|
|
log_info "Sending transaction..."
|
|
log_info " Contract: $CONTRACT"
|
|
log_info " Function: $FUNCTION"
|
|
log_info " Args: $ARGS"
|
|
log_info ""
|
|
|
|
# Send transaction with optimal gas
|
|
TX_OUTPUT=$(cast send "$CONTRACT" "$FUNCTION" $ARGS \
|
|
--rpc-url "$RPC_URL" \
|
|
--private-key "$PRIVATE_KEY" \
|
|
--gas-price "$OPTIMAL_GAS" \
|
|
--nonce "$CURRENT_NONCE" \
|
|
2>&1 || echo "FAILED")
|
|
|
|
if echo "$TX_OUTPUT" | grep -qE "(blockHash|transactionHash)"; then
|
|
TX_HASH=$(echo "$TX_OUTPUT" | grep -oE "(blockHash|transactionHash)[[:space:]]+0x[0-9a-fA-F]{64}" | awk '{print $2}' | head -1)
|
|
log_success "Transaction sent successfully!"
|
|
log_info " Hash: $TX_HASH"
|
|
log_info " Gas Price: $OPTIMAL_GAS_GWEI gwei"
|
|
log_info " Nonce: $CURRENT_NONCE"
|
|
exit 0
|
|
else
|
|
log_error "Transaction failed"
|
|
log_error "$TX_OUTPUT"
|
|
|
|
# Check for specific errors
|
|
if echo "$TX_OUTPUT" | grep -q "underpriced"; then
|
|
log_warn "Transaction underpriced"
|
|
log_info "Try with higher gas multiplier: GAS_MULTIPLIER=2.0 $0 $@"
|
|
elif echo "$TX_OUTPUT" | grep -q "nonce"; then
|
|
log_warn "Nonce issue detected"
|
|
log_info "Wait for pending transactions or use next nonce"
|
|
fi
|
|
|
|
exit 1
|
|
fi
|
|
|