#!/usr/bin/env bash # Deploy Smart Contract via eth_sendSignedTransaction (JSON-RPC) # Bypasses forge and uses direct JSON-RPC calls set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" source "$PROJECT_ROOT/.env" 2>/dev/null || source "$PROJECT_ROOT/../.env" 2>/dev/null || true RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}" PRIVATE_KEY="${PRIVATE_KEY:-}" GAS_PRICE="${1:-20000000000}" # 20 gwei GAS_LIMIT="${2:-10000000}" # 10M gas if [ -z "$PRIVATE_KEY" ]; then echo "Error: PRIVATE_KEY not set" exit 1 fi DEPLOYER=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || echo "") if [ -z "$DEPLOYER" ]; then echo "Error: Invalid PRIVATE_KEY" exit 1 fi echo "╔══════════════════════════════════════════════════════════════╗" echo "║ DEPLOY CONTRACT VIA eth_sendSignedTransaction ║" echo "╚══════════════════════════════════════════════════════════════╝" echo "" echo "RPC: $RPC_URL" echo "Deployer: $DEPLOYER" echo "Gas Price: $GAS_PRICE" echo "Gas Limit: $GAS_LIMIT" echo "" # Step 1: Compile contract echo "Step 1: Compiling contract..." TEMP_DIR=$(mktemp -d) cd "$TEMP_DIR" forge init --no-git --force . > /dev/null 2>&1 # Create minimal test contract cat > src/TestMinimal.sol << 'EOF' // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract TestMinimal { uint256 public x = 1; function setX(uint256 _x) external { x = _x; } } EOF forge build > /dev/null 2>&1 # Get bytecode BYTECODE=$(cat out/TestMinimal.sol/TestMinimal.json | jq -r '.bytecode.object' 2>/dev/null || echo "") if [ -z "$BYTECODE" ] || [ "$BYTECODE" = "null" ]; then echo "❌ Failed to compile contract" exit 1 fi echo "✅ Contract compiled (bytecode length: ${#BYTECODE} chars)" echo "" # Step 2: Get nonce echo "Step 2: Getting deployer nonce..." NONCE=$(cast nonce "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") NONCE_HEX=$(printf "0x%x" "$NONCE") echo "Nonce: $NONCE ($NONCE_HEX)" echo "" # Step 3: Get chain ID echo "Step 3: Getting chain ID..." CHAIN_ID=$(cast chain-id --rpc-url "$RPC_URL" 2>/dev/null || echo "138") CHAIN_ID_HEX=$(printf "0x%x" "$CHAIN_ID") echo "Chain ID: $CHAIN_ID ($CHAIN_ID_HEX)" echo "" # Step 4: Create transaction echo "Step 4: Creating transaction..." echo "Transaction details:" echo " From: $DEPLOYER" echo " To: null (contract creation)" echo " Value: 0" echo " Data: $BYTECODE (first 100 chars: ${BYTECODE:0:100}...)" echo " Gas: $GAS_LIMIT" echo " Gas Price: $GAS_PRICE" echo " Nonce: $NONCE" echo " Chain ID: $CHAIN_ID" echo "" # Step 5: Deploy using cast send directly echo "Step 5: Deploying contract using cast send..." echo "This uses eth_sendTransaction internally..." echo "" TX_HASH=$(cast send --private-key "$PRIVATE_KEY" \ --rpc-url "$RPC_URL" \ --gas "$GAS_LIMIT" \ --gas-price "$GAS_PRICE" \ --nonce "$NONCE" \ --value 0 \ --data "$BYTECODE" \ --legacy \ --json 2>/dev/null | jq -r '.transactionHash // empty' 2>/dev/null || echo "") if [ -z "$TX_HASH" ]; then echo "❌ Failed to send transaction via cast send" echo "" echo "Trying alternative: Create raw transaction and send via JSON-RPC..." # Create raw transaction RAW_TX=$(cast tx --from "$DEPLOYER" \ --to "" \ --nonce "$NONCE" \ --gas "$GAS_LIMIT" \ --gas-price "$GAS_PRICE" \ --value 0 \ --data "$BYTECODE" \ --chain "$CHAIN_ID" \ --rpc-url "$RPC_URL" \ --json 2>/dev/null | jq -r '.raw // empty' 2>/dev/null || echo "") if [ -n "$RAW_TX" ]; then echo "✅ Created raw transaction" echo "Sending via eth_sendRawTransaction..." RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" \ --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_sendRawTransaction\",\"params\":[\"$RAW_TX\"],\"id\":1}" \ "$RPC_URL" 2>&1) TX_HASH=$(echo "$RESPONSE" | jq -r '.result // empty' 2>/dev/null || echo "") if [ -z "$TX_HASH" ] || [ "$TX_HASH" = "null" ]; then ERROR=$(echo "$RESPONSE" | jq -r '.error.message // empty' 2>/dev/null || echo "") echo "❌ Error: $ERROR" echo "Response: $RESPONSE" fi fi fi # Step 6: Wait for confirmation if [ -n "$TX_HASH" ] && [ "$TX_HASH" != "null" ]; then echo "✅ Transaction sent: $TX_HASH" echo "" echo "Waiting for confirmation..." sleep 15 RECEIPT=$(cast receipt "$TX_HASH" --rpc-url "$RPC_URL" --json 2>/dev/null || echo "") if [ -n "$RECEIPT" ]; then STATUS=$(echo "$RECEIPT" | jq -r '.status // empty' 2>/dev/null || echo "") CONTRACT=$(echo "$RECEIPT" | jq -r '.contractAddress // empty' 2>/dev/null || echo "") GAS_USED=$(echo "$RECEIPT" | jq -r '.gasUsed // empty' 2>/dev/null || echo "") echo "Transaction Receipt:" echo " Status: $STATUS" echo " Gas Used: $GAS_USED" echo " Contract Address: $CONTRACT" echo "" if [ "$STATUS" = "0x1" ]; then echo "✅ Contract deployed successfully!" # Verify contract exists CODE=$(cast code "$CONTRACT" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$CODE" ] && [ "$CODE" != "0x" ] && [ ${#CODE} -gt 100 ]; then echo "✅ Contract verified on-chain (${#CODE} chars)" echo "" echo "Contract Address: $CONTRACT" echo "Save this address for future use!" else echo "⚠️ Contract code not found on-chain" fi else echo "❌ Transaction failed (status: $STATUS)" echo "Contract Address: $CONTRACT" echo "" echo "If DEBUG API is enabled, get revert reason:" echo " curl -X POST -H 'Content-Type: application/json' \\" echo " --data '{\"jsonrpc\":\"2.0\",\"method\":\"debug_traceTransaction\",\"params\":[\"$TX_HASH\",{\"tracer\":\"callTracer\"}],\"id\":1}' \\" echo " $RPC_URL | jq" fi else echo "⏳ Transaction not yet confirmed" echo "Transaction Hash: $TX_HASH" echo "Check status later with: cast receipt $TX_HASH --rpc-url $RPC_URL" fi else echo "❌ Could not deploy contract" echo "" echo "Troubleshooting:" echo " 1. Check deployer balance: cast balance $DEPLOYER --rpc-url $RPC_URL" echo " 2. Verify RPC is accessible: cast block-number --rpc-url $RPC_URL" echo " 3. Check network allows contract creation" fi cd "$PROJECT_ROOT" rm -rf "$TEMP_DIR" echo ""