Last Updated: 2025-11-15
The MiTeddy mint bot is a high-performance, low-latency transaction execution system designed to win competitive NFT mints by being first-in-window.
src/main-rbf.ts) is now the PRIMARY/PRODUCTION bot. The Original Bot (src/index.ts) is LEGACY/DEPRECATED.
Reason for Switch: Original bot failed in ALL recent production runs (2025-11-13, 2025-11-14 (3x), 2025-11-15). RBF bot has superior architecture (block-event driven, no time prediction, simpler code).
Status: ✅ PRIMARY - Use this for all production runs
Command: npm run start:rbf
Operates in "Shotgun RBF Strategy" - block-event driven architecture with escalating gas (200x → 1,223x).
Key Features:
- Block-event driven detection (no time prediction)
- 20 pulses per 2-second block
- Gas escalation: 200x base → 1,223x maximum
- Receipt-based nonce discipline
- 80 broadcast attempts per nonce (20 pulses × 4 RPCs)
- Success rate: 50-60% in extreme competition
Status: ❌ DEPRECATED - Historical reference only
Command: npm start (not recommended)
Operates in "Battering Ram Mode" - maximum aggression from the first transaction (50.0x gas).
Why Deprecated: Failed in all recent production runs. Complex timing logic, time-based spam disabled, view-based detection issues.
See RBF Architecture Review for a complete comparison of both implementations.
- No time prediction needed: Reacts to on-chain events
- Parallel WebSocket subscriptions: 0-50ms detection latency
- Multiple RPCs monitor simultaneously for redundancy
- Window detection via
availableMintAmount()on each block
- 20 pulses per 2-second block (same nonce, escalating gas)
- Gas escalation: 200x base (5,000 Gwei) → 1,223x (30,580 Gwei)
- Compound gas bumps: 5% increase per pulse (200x → 210x → 220.5x → ...)
- 80 broadcast attempts per nonce (20 pulses × 4 RPCs)
- Waits for transaction receipt before incrementing nonce
- Prevents nonce conflicts and stale transaction issues
- More reliable than immediate nonce refresh
- Parallel WebSocket subscriptions: 0-50ms detection
- Pre-cached calldata and gas params: 0ms calculation
- Multi-RPC parallel broadcast: All RPCs simultaneously
- Compound gas escalation pre-calculated
- Multi-RPC parallel broadcast (4+ RPCs per pulse)
- RPC pool with automatic fallback
- Circuit breaker for RPC health
- State persistence for crash recovery
- No gradual ramp-up: First transaction uses 50.0x gas (12,500 Gwei) [LEGACY]
- All paths (RBF, shadow branch, spam) start at maximum [LEGACY]
- Pre-signed transactions ready at maximum gas [LEGACY]
- Pre-warming: 30s before expected unlock [LEGACY]
- Time-based spam: 25s before, 60s after expected [LEGACY - DISABLED]
- View-based detection: Real-time window detection [LEGACY]
- Buffer for early opening: -8000ms offset [LEGACY]
- Loads environment variables
- Validates settings
- Builds contract ABIs dynamically
- Provides type-safe configuration
CRITICAL (2025-11-14): RPC architecture completely redesigned after catastrophic single-point-of-failure at 23:11 window. See POST_MORTEM_2025-11-14_2311_COMPLETE_FAILURE.md.
-
RPC Pool (
pool.ts): NEW - Multi-RPC client pool with automatic fallback- Purpose: Eliminates single RPC failure = total blindness
- Features:
- Automatic fallback: tries all healthy RPCs in order
- 429 rate limit detection → temporary blacklist (respects retry-after header)
- Circuit breaker integration for long-term health tracking
- Pre-created clients for all RPCs (zero latency on fallback)
- Usage: All view function calls, nonce fetches, block queries use RPCPool.callWithFallback()
- Impact: Window detection survived 2025-11-14 23:10:49 PublicNode rate limit cascade with automatic QuikNode fallback
-
Circuit Breaker (
circuitBreaker.ts): Monitors RPC health, blacklists failing RPCs- Blacklists RPCs for 60s if: p95 latency > 400ms, >10% error rate, ≥2 consecutive timeouts, ≥3 malformed responses
- Auto-unblacklists after cooldown period
- Integrated with RPCPool for unified health tracking
-
RPC Metrics Persistence (
metrics-persistence.ts): Warm-start optimization- Saves RPC performance metrics between sessions
- Loads historical data on startup (avoids cold-start learning)
-
RPC Roster (
roster.ts): Selects top N healthy RPCs for broadcasts -
Broadcast System: Parallel broadcast to multiple RPCs with retry logic (exponential backoff: 2s, 4s, 8s)
- Block Time Synthesizer (
synth.ts): Predicts unlock block from samples- Samples
remainingUnlockTime()across multiple blocks - Uses linear regression to predict unlock block number
- Calculates confidence scores (R-squared)
- Estimates unlock timestamp from block predictions
- Requires minimum 3 samples, optimal with 8 samples
- Logs predicted unlock block when < 60s to unlock
- Samples
- Time Utils (
time-utils.ts): Timezone handling, formatting - Units (
time/units.ts): Type-safe time unit conversions- Prevents accidental mixing of seconds and milliseconds
sec(),ms(),isLessThanSeconds(),assertSeconds()- Critical for avoiding timing bugs (contract returns seconds, not milliseconds)
- Gas Utilities (
gasUtils.ts): Gas estimation, calculation, and management (673 lines) - Nonce Utilities (
nonceUtils.ts): Nonce management and caching - Simulation Utilities (
simulationUtils.ts): Contract simulation and failure detection (257 lines) - View Utilities (
viewUtils.ts): Contract view function calls with caching - Pre-Signing Utilities (
preSignUtils.ts): Transaction pre-signing and nonce-aware management - Balance Utilities (
balanceUtils.ts): Balance checking and shortfall tracking - RPC Utilities (
rpcUtils.ts): RPC broadcasting and health tracking - NFT Utilities (
nftUtils.ts): NFT ownership and approval management - Pre-Warm Utilities (
preWarmUtils.ts): Pre-warming transaction management (598 lines)
Impact: Extracted 3,575+ lines from src/index.ts into 9 focused utility modules, reducing main file by 36% (6,178 → 3,933 lines). Phase 4a complete: Pre-warm functions now use wrapper pattern delegating to preWarmUtils.ts implementations.
- Window Detection: View functions + time-based spam (uses
viewUtils) - Pre-warming: Escalating gas transactions before unlock (uses
preWarmUtilswrapper pattern)preWarmTransaction()andsetupPreWarmScheduler()are wrappers delegating topreWarmUtils.ts- Implementation functions:
preWarmTransactionImpl()andsetupPreWarmSchedulerImpl()
- Fire Transaction: Main execution path with dual-branch
- RBF Loop: Replace-by-fee with same nonce
- Shadow Branch: New nonce with higher gas
Note: Many functions now delegate to utility modules. Main file reduced to 3,933 lines (from 6,178, -36%). Phase 4a complete: Pre-warm utilities fully extracted with wrapper pattern.
- Entry Point:
src/main-rbf.ts(~380 lines - orchestration only) - Window Detection:
src/detection/window.ts- Block-based event-driven (0s timing error)- CRITICAL (2025-11-14): Uses RPCPool for view functions (automatic fallback on RPC failure)
- Hybrid monitoring: WebSocket (primary) + HTTP polling (backup, auto-disables when WS healthy)
- Rate limit resilience: View functions survive single RPC 429 errors by falling back to other RPCs
- RBF Engine:
src/rbf/engine.ts- Shotgun strategy (20 parallel replacements) - Nonce Management:
src/nonce/manager.ts- Receipt-based discipline- CRITICAL (2025-11-14): Added nonce cascade protection (max +5 delta, 200ms retry delay)
- Gas Pricing:
src/gas/adaptive.ts- Compound escalation with pre-calculation - Error Handling:
src/errors/handlers.ts- Centralized error classification - Parallel WebSocket:
src/detection/parallel-ws.ts- Multi-RPC redundancy - RPC Pool:
src/rpc/pool.ts- Multi-RPC fallback with 429 detection (NEW 2025-11-14)
Key Difference: Modular architecture vs monolithic. See RBF Architecture Review for complete comparison.
- Shared State (
phases/sharedState.ts): Centralized bot state (BotState singleton)- All 51 global variables migrated to
botStatesingleton - Type-safe state management with full TypeScript typing
- Testable (can mock
botStatefor testing)
- All 51 global variables migrated to
- Lock File (
lockfile.ts): Single-instance enforcement- Atomic lock acquisition using
O_CREAT | O_EXCLflag - Stale lock detection (checks if process still running)
- Auto-cleanup on process exit
- Atomic lock acquisition using
- Nonce Store (
nonceStore.ts): Persistent nonce tracking - Cache (
cache.ts): LRU cache for view functions
- Last-Second Fetch: Nonce fetched immediately before signing (line 2566-2575)
- Only activates during spam window or within 10 seconds of unlock
- Minimizes stale nonce window
- Early Fetch: Nonce fetched earlier for planning (line 2474-2481)
- Recovery: Immediate nonce refresh on "nonce too low" errors (line 2629-2639)
- Pre-warming: Nonce refreshed before each pre-warm execution (line 3924-3930)
- Why: Prevents 100% broadcast failure from stale nonces
- Standard Log (
log.ts): Console output (also preserved to detailed log file) - Hot Path Log (
log-hotpath.ts): Suppressed logging during critical window - Detailed Log (
detailed-log.ts): Structured JSON logging with async file writes- Console Log Preservation: All console logs automatically saved to file (zero time impact)
- Async Writes: Non-blocking file I/O ensures no performance impact
- RPC Metrics (
rpcMetrics.ts): Tracks RPC broadcast performance- Records successes, failures, timeouts per RPC
- Provides aggregated metrics summaries
- Used for RPC health monitoring and optimization
- Simulation Results (
simulationResult.ts): Type-safe simulation outcome handling- Structured success/failure types
- Type guards for error classification
- Distinguishes between locked contracts, timeouts, and infrastructure failures
- Used by simulation gate to prevent wasted gas on locked windows
- Budget Tracker (
budgetTracker.ts): Nuclear gas budget monitoring- Tracks balance shortfalls when nuclear transactions can't be sent
- Provides retry logic with cooldown periods
- Formats BERA amounts for user-friendly logging
- Prevents repeated attempts when wallet is underfunded
- Environment Utils (
env-utils.ts): Consistent environment variable parsingparseBooleanEnv(): Parses boolean values from strings (true/1/yes/on)getBooleanEnv(): Gets boolean env var with default- Handles various truthy/falsy string formats
- Time Utils (
time-utils.ts): Timezone handling and formatting- PST/PDT timezone conversions
- Timestamp formatting for logs
- RPC Utils (
rpc-utils.ts): RPC helper functions- Nonce fetching with fallback
- Consensus nonce calculation
- Parallel RPC requests
STARTUP
↓
INITIALIZATION (wrapped in Promise.race with 2-minute timeout)
↓
1. Acquire Lock (single-instance)
↓
2. Load Configuration
↓
3. Initialize RPC Clients (HTTP + WebSocket)
↓
4. Verify Network (Chain ID)
↓
5. Check Pending Transactions
↓
6. Verify NFT Ownership
↓
7. Set Approvals (if needed)
↓
8. Pre-cache Calldata & Gas Limit (parallel RPC racing, 5s timeout)
↓
9. Pre-warm Gas Params (all multipliers in parallel, 2s timeout each)
↓
10. Pre-sign Transactions (50.0x + bump levels)
↓
11. Setup Time-Based Spam (if enabled)
↓
12. Setup Pre-warm Scheduler
↓
13. Setup WebSocket Block Subscription
↓
14. Initialization Complete (~11.4 seconds)
↓
MAIN LOOP (runs after initialization completes)
↓
MAIN LOOP
├─ Check View Functions (paused, available, unlockTime)
├─ Update Block Timing (predict unlock block)
├─ Pre-warm Transaction (if T-30s to T-0s)
├─ Spam Transaction (if in spam window)
├─ Detect Window Open (unlockTime === 0n && !paused)
└─ Fire Transaction (if window open)
↓
FIRE TRANSACTION
├─ Try Pre-signed Nuclear TX (50.0x)
├─ Launch Dual-Branch (if enabled)
│ ├─ Primary: RBF Loop (50.0x, same nonce)
│ └─ Shadow: New Nonce (75.0x, nonce+1)
└─ Broadcast to All RPCs (parallel)
↓
SUCCESS
├─ Wait for Receipt
├─ Verify Transfer Event
├─ Clear State
└─ Exit
- Concept: Hit with maximum force immediately
- Implementation: All gas paths start at 50.0x
- Why: Competitive mints require maximum gas from first transaction
- Primary Branch: RBF with same nonce (50.0x gas)
- Shadow Branch: New nonce with higher gas (75.0x = 50.0x × 1.5)
- Why: Redundancy - if primary fails, shadow succeeds
- Timing: Starts 300s (5 min) before expected unlock (configurable via
PRE_WARM_START_SECONDS) - Gas Strategy:
- Early steps:
PRE_WARM_LOW_MULTIPLIER(default 20×) to reduce costs - Later steps: Escalate to 50.0× (Battering Ram Mode)
- All steps at 50.0× in Battering Ram Mode (default)
- Early steps:
- Simulation Gate:
eth_callscouts prevent locked-window spam (Codex refactor 2025-11-13)- Context-Aware Bypass: Tracks consecutive infrastructure failures and bypasses gate when within
SIMULATION_FORCE_BYPASS_SECONDS(default 5s) of unlock - Infrastructure Failure Tracking: Distinguishes between locked contracts and RPC failures, auto-bypasses on consecutive infra failures near unlock
- Context-Aware Bypass: Tracks consecutive infrastructure failures and bypasses gate when within
- Nonce Refresh: Refreshed both at scheduler setup AND right before each execution
- Dual Nonce Pre-Signing: Pre-signs both current and next nonce for instant promotion when nonce advances (Codex 2025-11-13)
- Why: Transaction in mempool before window opens = instant execution
- Timing: 25s before expected, 60s after
- Rate: 40 tx/sec (every 25ms)
- Gas: 50.0x on all transactions
- Why: Maximum coverage for timing variations
- Strategy: Tiered fan-out with force full fan-out option (Codex refactor 2025-11-13)
- Top N fastest RPCs fire first (
RPC_PRIMARY_FANOUT, default 3) - Remaining RPCs fire if no hash lands
- Force Full Fan-Out: Critical windows (pre-signed nuclear, primary fire, shadow branch) bypass filters and send to all RPCs
- Top N fastest RPCs fire first (
- Selection: Circuit breaker selects top N healthy RPCs
- BroadcastOptions: Configurable RPC filtering, force full fan-out, and fanout reason tracking
- Why: Redundancy + speed (first RPC to accept wins) + reduced RPC load + maximum coverage in critical moments
Contract View Functions
├─ paused() → boolean
├─ availableMintAmount() → bigint
└─ remainingUnlockTime() → bigint (seconds)
↓
Cache (LRU, 5min TTL)
↓
Window Detection Logic
├─ unlockTime === 0n → Window open
├─ !paused → Contract active
└─ available > 0 → Slots available (optional check)
↓
Fire Transaction
Block Fetch (latest/pending)
├─ baseFeePerGas → bigint
└─ timestamp → number
↓
Gas Calculation
├─ priorityFee = PRIORITY_FEE_GWEI × multiplier
├─ maxFee = (baseFee × BASE_MULTIPLIER × multiplier) + priorityFee
└─ Apply caps (MAX_PRIORITY_FEE_GWEI, MAX_FEE_PER_GAS_GWEI, FEE_MAX_GWEI_CAP)
↓
Cache (by multiplier)
↓
Use in Transaction
Calldata (pre-cached)
├─ Function: mint(uint256,uint256)
├─ Args: [teddyId, miberaId]
└─ Encoded: 0x1b2ef1ca...
↓
Gas Params (pre-cached or calculated)
├─ maxFeePerGas: 12500 Gwei (50.0x)
└─ maxPriorityFeePerGas: 12500 Gwei
↓
Nonce (cached or fresh)
├─ Replace strategy: Same nonce (for RBF)
├─ Increment strategy: Fresh nonce (for shadow)
└─ Dual Pre-Signing: Both current and next nonce pre-signed (Codex 2025-11-13)
↓
Sign Transaction
├─ Last-second nonce fetch: ~50ms (critical fix 2025-11-14)
├─ Pre-signed (if available): Instant
├─ Next-nonce promotion: Instant (if nonce advanced)
└─ On-the-fly: 50-100ms (with retry up to PRE_SIGN_MAX_ATTEMPTS=2)
↓
Broadcast (parallel to all RPCs)
├─ Force Full Fan-Out: Critical windows bypass filters (Codex 2025-11-13)
├─ HTTP RPCs: 50-150ms
└─ WebSocket RPCs: 50-150ms
↓
Transaction in Mempool
{
"nonce": 42,
"lastTxHash": "0x...",
"windowOpen": false,
"lastError": null
}Purpose: Crash recovery, nonce tracking, error logging
- Purpose: Single-instance enforcement
- Format: PID + timestamp
- Cleanup: Auto-released on exit
- View Cache: LRU, 256 entries, 5min TTL
- Gas Params Cache: Map by multiplier (100, 200, 300, etc.)
- RPC Broadcast Cache: LRU, prevents duplicate broadcasts
- Circuit Breaker: Tracks failures, blacklists after threshold
- Fallback: Try next RPC if one fails
- Timeout: 900ms per RPC request
- RBF Loop: Replace with higher gas (Codex 2025-11-13)
- Minimum Bump: Enforces at least
minBumpPct(default 10%) orminBumpGwei(1 gwei) increase - Replacement Handling: Tightened to ensure valid replacements that meet network requirements
- Minimum Bump: Enforces at least
- Shadow Branch: New nonce if primary fails
- Retry: Up to MAX_BUMPS attempts
- Pre-Sign Retry: Up to PRE_SIGN_MAX_ATTEMPTS (default 2) when signing pre-signed transactions
- Crash Recovery: Load state on startup, resume if needed
- Pending TX Check: Verify if previous transaction was included
- Nonce Sync: Update nonce if behind chain
- Block Detection: 0-50ms (WebSocket) or 50-100ms (polling)
- Window Detection: 50-100ms (view function call)
- Transaction Signing: 0ms (pre-signed) or 50-100ms (on-the-fly)
- Broadcast: 50-150ms (parallel to all RPCs)
- Total: 100-400ms from block to mempool
- Spam Rate: 40 tx/sec (25ms interval)
- Pre-warm Rate: 1 tx/sec (escalating gas)
- RPC Broadcast: Parallel (all RPCs simultaneously)
- Memory: ~50-100MB (caches, state)
- CPU: Low (mostly I/O bound)
- Network: Moderate (RPC calls, broadcasts)
- Maximum Gas: 50.0x (12,500 Gwei) from start
- Early Start: 30s pre-warm, 25s spam
- Long Coverage: 60s after expected unlock
- High Rate: 40 tx/sec spam
- Gas multipliers (BASE_MULTIPLIER, MAX_PRIORITY_FEE_GWEI)
- Timing (SPAM_START_SECONDS, PRE_WARM_START_SECONDS)
- RPC selection (RPC_SCORE_TOP_N, circuit breaker thresholds)
- Hot Wallet Only: Never use main wallet
- Minimal Funding: Only gas + NFTs needed
- Private Key: Stored in .env (gitignored)
- Dry Run Mode: Test without sending transactions
- State Validation: Verify ownership before minting
- Error Handling: Comprehensive try-catch coverage
npm start -- --dry-run- Simulates all operations
- No transactions sent
- Verifies configuration
npm start -- --mock-unlock- Simulates window opening after 3s
- Tests transaction firing
- No actual contract calls
The bot includes a comprehensive testing framework for rapid development and validation:
- Time Compression: Test 5-minute phases in 30 seconds (10x-1000x compression)
- 22 Failure Scenarios: Predefined tests for all known failure modes
- Phase Simulation: Test phases independently or in sequence
- Failure Injection: Trigger specific failures on demand
- State Validation: Verify bot state at phase boundaries
- Performance Profiling: Track memory, CPU, event loop lag
- Coverage Tracking: Monitor which failure modes have been tested
Quick Start:
npm run test:bot -- --test-mode=init
npm run test:critical
npm run test:bot -- --test-mode=full --time-compression=10See docs/testing/README.md for complete documentation.
- Add to
RPCS_HTTPorRPCS_WSin.env - Circuit breaker automatically monitors
- Roster automatically includes in selection
- Modify
getGasParams()insrc/index.ts - Update
BASE_MULTIPLIERand caps - Adjust pre-warm steps
- Modify
checkViewFunctions()insrc/index.ts - Update
simulateOpen()for custom view functions - Adjust window detection logic
Version: 1.3
Recent Improvements (2025-11-14 - Phase 4a Refactoring):
- ✅ Phase 4a Complete: Pre-warm utilities extracted with wrapper pattern (-349 lines)
- ✅ Compilation Fixes: All 47 TypeScript errors fixed (47 → 0 errors)
- ✅ Build Status: Zero TypeScript errors, clean compilation
- ✅ Code Organization:
src/index.tsreduced to 3,933 lines (36% reduction from original)
Recent Improvements (2025-11-13 - Codex):
- ✅ BroadcastOptions: Force full fan-out for critical windows (pre-signed nuclear, primary fire, shadow branch)
- ✅ Simulation Gate Context: Context-aware bypass tracks consecutive infrastructure failures and auto-bypasses near unlock
- ✅ Dual Nonce Pre-Signing: Pre-signs both current and next nonce for instant promotion when nonce advances
- ✅ Replacement Handling: Enforces minimum bump requirements (minBumpPct/minBumpGwei) for valid replacements
- ✅ Pre-Sign Retry: Retry logic (PRE_SIGN_MAX_ATTEMPTS=2) for transaction signing failures
- ✅ Enhanced Error Reporting: Categorized broadcast errors (skipped RPCs, rate limits, duplicates, replacements)
Previous Improvements (2025-11-12):
- ✅ Initialization: ~11.4 seconds (optimized from 3-4 minutes) - ~20x faster
- ✅ Console log preservation: All console logs automatically saved to detailed log file
- ✅ Initialization timeout: 2-minute max prevents indefinite hangs
- ✅ Parallel execution: Gas pre-warming runs all steps simultaneously
- ✅ Progress visibility: All initialization steps logged with duration