Binance Alpha Farm Agent

Automated trading agent for Binance Alpha Farm with robust state management and task-step architecture.

📋 Table of Contents

🏗️ System Architecture

The agent follows a hybrid architecture combining centralized task definitions with dedicated step execution:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   AppState      │    │   TaskRunner    │    │   StepRunner    │
│   (State Mgmt)  │◄──►│   (Task Mgmt)   │◄──►│   (Step Exec)   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   StateWatchers │    │ TASK_DEFINITIONS│    │  Step Functions │
│   (Observers)   │    │   (Task Config) │    │   (Step Logic)  │
└─────────────────┘    └─────────────────┘    └─────────────────┘

Key Components:

  • AppState: Centralized state management with observer pattern
  • TaskRunner: Handles task polling and coordination
  • StepRunner: Executes individual steps with navigation handling
  • StateWatchers: React to state changes (page, login, bot status)
  • TASK_DEFINITIONS: Centralized task configurations
  • Step Functions: Modular step logic (LoginSteps, OrderHistorySteps, BalanceSteps)

📁 Code Structure & Module Order

The code follows this specific order for maintainability:

1. Configuration & Constants

// ====== CONFIGURATION ======
const CONFIG = { ... }

// ====== ENUMS & CONSTANTS ======
const BOT_STATUS = { ... }
const TASK_TYPES = { ... }
const STEP_TYPES = { ... }
const LOGIN_METHOD = { ... }
const ORDER_TYPE = { ... }
const ORDER_STATUS = { ... }
const BALANCE_FORMAT = { ... }
const GAS_FEE_TYPE = { ... }

2. State Management

// ====== STATE MANAGEMENT ======
class AppState { ... }

// ====== STORAGE MODULE ======
const STORAGE = { ... }

3. Utility Modules

// ====== TL UTILITY MODULE ======
const TL = { ... }

// ====== BAF API MODULE ======
const BAF = { ... }

// ====== BINANCE PAGE MODULE ======
const BINANCE = { ... }

4. Step Functions (Modular Logic)

// ====== STEP FUNCTIONS ======
const LoginSteps = { ... }
const OrderHistorySteps = { ... }
const BalanceSteps = { ... }

5. Task Definitions

// ====== TASK DEFINITIONS ======
const TASK_DEFINITIONS = { ... }

6. Core Execution Engine

// ====== STEP RUNNER ======
class StepRunner { ... }

// ====== TASK RUNNER ======
const TaskRunner = { ... }

// ====== STATE WATCHERS ======
const StateWatchers = { ... }

7. UI & Initialization

// ====== UI & MENU ======
async function createGM_Menu() { ... }

// ====== MONITORING ======
async function heartbeat_report() { ... }
async function monitorPageChanges() { ... }

// ====== INITIALIZATION ======
async function initialize() { ... }

🔄 Task-Step System

Task Types

const TASK_TYPES = {
    LOGIN: 'login',
    GET_ORDER_HISTORY: 'get-order-history', 
    GET_BALANCE: 'get-balance',
    SWAP: 'swap',
    NO_TASK: 'no-task'
};

Step Types by Task

Login Task

const STEP_TYPES = {
    LOGIN: {
        NAVIGATE_TO_LOGIN: 'NavigateToLogin',
        SELECT_QR_CODE: 'SelectQRCode', 
        WAIT_FOR_LOGIN: 'WaitForLogin'
    }
};

Order History Task

const STEP_TYPES = {
    GET_ORDER_HISTORY: {
        NAVIGATE_TO_ORDER_HISTORY: 'NavigateToOrderHistory',
        EXTRACT_ORDER_DATA: 'ExtractOrderData',
        SEND_ORDER_DATA: 'SendOrderData'
    }
};

Balance Task

const STEP_TYPES = {
    GET_BALANCE: {
        NAVIGATE_TO_BALANCE: 'NavigateToBalance',
        EXTRACT_BALANCE_DATA: 'ExtractBalanceData',
        SEND_BALANCE_DATA: 'SendBalanceData'
    }
};

Task Data Examples

Login Task

{
    type: TASK_TYPES.LOGIN,
    id: "login_123",
    data: {
        method: LOGIN_METHOD.QR_CODE,
        timeout: 300000, // 5 minutes
        retry_count: 3
    }
}

Order History Task

{
    type: TASK_TYPES.GET_ORDER_HISTORY,
    id: "order_history_456",
    data: {
        limit: 50,
        date_from: "2024-01-01",
        date_to: "2024-12-31",
        status: ORDER_STATUS.COMPLETED
    }
}

Balance Task

{
    type: TASK_TYPES.GET_BALANCE,
    id: "balance_789",
    data: {
        currencies: ["USDT", "BTC", "ETH"],
        format: BALANCE_FORMAT.DECIMAL
    }
}

🤖 Bot Status Flow

Status Transitions

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│ ready-for-new-  │────►│ performing-     │────►│ ready-for-new-  │
│ tasks           │     │ tasks           │     │ tasks           │
└─────────────────┘     └─────────────────┘     └─────────────────┘
         │                       │                       ▲
         ▼                       ▼                       │
┌─────────────────┐     ┌─────────────────┐             │
│ pause-          │◄────┤ Task Complete   │─────────────┘
│ automation      │     │ or Error        │
└─────────────────┘     └─────────────────┘

Flow Description:

  1. ready-for-new-tasks: Bot polls server for new tasks
  2. performing-tasks: Bot executes task steps
  3. pause-automation: Bot stops (manual or error)
  4. Back to ready-for-new-tasks: After task completion or resume

Task Polling Logic:

// Poll only when ready-for-new-tasks
if (bot_status === BOT_STATUS.READY_FOR_NEW_TASKS) {
    const task = await BAF.getTask();
    if (task.type === TASK_TYPES.NO_TASK) {
        // Sleep for interval before next poll
        await delay(CONFIG.task_poll_interval);
    } else {
        // Start task execution
        bot_status = BOT_STATUS.PERFORMING_TASKS;
    }
}

🎯 State Management

AppState Structure

class AppState {
    data = {
        // Server configuration
        server_mode: 'prod',
        server: null,
        
        // Page state
        current_page: null,
        is_logged_in: false,
        
        // Bot status
        bot_status: BOT_STATUS.READY_FOR_NEW_TASKS,
        
        // Task state
        current_task: null,
        task_data: null,
        current_step: null,
        step_data: null,
        
        // UI state
        is_loading: false,
        error_message: null
    };
}

Observer Pattern

// Subscribe to state changes
APP_STATE.subscribe('bot_status', StateWatchers.onBotStatusChange);
APP_STATE.subscribe('current_page', StateWatchers.onCurrentPageChange);
APP_STATE.subscribe('is_logged_in', StateWatchers.onLoginStateChange);

// Notify observers on state change
await APP_STATE.update({ bot_status: BOT_STATUS.PERFORMING_TASKS });

🧭 Navigation State Management

Navigation State Structure

{
    navigating: true,
    navigation_start: 1703123456789,
    navigation_target: "https://accounts.binance.com/login",
    task_id: "task_123",
    step_index: 0
}

Navigation Flow

1. Step executes → ctx.goto('/login')
   ↓
2. Mark navigating = true
   ↓
3. Save navigation state to storage
   ↓
4. window.location.href = '/login'
   ↓
5. Page reloads, userscript restarts
   ↓
6. resumeTask() → detect navigation state
   ↓
7. handleNavigationResume() → check target page
   ↓
8. Skip navigation step, continue next step

guardDoubleRun Helper

// Prevents double execution during navigation
await TL.guardDoubleRun(ctx, async () => {
    // Step logic here
    if (!BINANCE.isOnLoginPage()) {
        BINANCE.navigateToLogin();
    }
});

🛠️ Helper Functions

TL Utility Module

const TL = {
    // Logging
    log: (level, message, data) => { ... },
    debug: (level, message, data) => { ... },
    error: (level, message, error) => { ... },
    
    // Utilities
    delay: (ms) => new Promise(resolve => setTimeout(resolve, ms)),
    notification: (title, text, timeout) => { ... },
    
    // Navigation guard
    guardDoubleRun: async (ctx, stepLogic) => { ... },
    
    // DOM helpers
    dom: {
        isVisible: (el) => { ... },
        isDisabled: (el) => { ... },
        click: (el) => { ... },
        scrollToView: (el, behavior) => { ... }
    },
    
    // Network helpers
    net: {
        gmRequest: (url, init) => { ... }
    }
};

TaskRunner Helpers

const TaskRunner = {
    // Task validation
    validateEnum: (value, enumObj, defaultValue) => { ... },
    validateTaskData: (taskType, taskData) => { ... },
    
    // Task state
    getCurrentTaskData: () => { ... },
    updateTaskData: (newData) => { ... },
    isCurrentTaskType: (taskType) => { ... },
    
    // Task execution
    checkForNewTasks: async () => { ... },
    continueCurrentStep: async () => { ... }
};

StepRunner Context

// Context passed to step functions
{
    task_id: "task_123",
    task_type: TASK_TYPES.LOGIN,
    task_data: { method: LOGIN_METHOD.QR_CODE },
    step_data: {},
    is_logged_in: false,
    current_page: BINANCE_PAGES.LOGIN,
    current_step_name: "NavigateToLogin",
    
    // Helper methods
    done: (result) => this.completeTask(result),
    goto: (url) => this.navigateTo(url),
    wait: (ms) => this.wait(ms),
    retry: (fn, maxAttempts) => this.retry(fn, maxAttempts)
}

⚙️ Configuration

Server Configuration

const CONFIG = {
    heartbeat_interval: 10000,    // 10 seconds
    task_poll_interval: 10000,    // 10 seconds
    is_debug: true,
    servers: {
        local: { 
            label: '🏠 Local', 
            url: 'http://localhost:3000' 
        },
        prod: { 
            label: '🌐 Prod', 
            url: 'https://baf.thuanle.me' 
        }
    }
};

Storage Keys

const STORAGE = {
    key_token: 'baf-agent-token',
    key_server_mode: 'baf-server-mode',
    key_bot_status: 'baf-bot-status',
    key_navigation_state: 'baf-navigation-state'
};

🌐 API Endpoints

BAF API Methods

const BAF = {
    // Server management
    getServer: async () => { ... },
    getHost: async () => { ... },
    
    // API requests
    request: async (method, path, options) => { ... },
    
    // Task management
    getTask: async () => { ... },
    submitTaskResult: async (taskId, result) => { ... },
    
    // Heartbeat
    heartbeat: async (data) => { ... }
};

Page Detection

const BINANCE = {
    // Page detection
    detectPage: () => { ... },
    isOnLoginPage: () => { ... },
    isOnAlphaSwapPage: () => { ... },
    isOnAlphaOrderHistoryPage: () => { ... },
    isLoggedIn: async () => { ... },
    
    // Navigation
    navigateToLogin: () => { ... },
    navigateToAlphaSwap: () => { ... },
    navigateToAlphaOrderHistory: () => { ... }
};

🚀 Development Notes

Adding New Tasks

  1. Define Task Type:
const TASK_TYPES = {
    // ... existing types
    NEW_TASK: 'new-task'
};
  1. Create Step Functions:
const NewTaskSteps = {
    matchStep1: (url, ctx) => { /* match logic */ },
    runStep1: async (ctx) => {
        await TL.guardDoubleRun(ctx, async () => {
            // Step logic
        });
    }
};
  1. Add Task Definition:
const TASK_DEFINITIONS = {
    [TASK_TYPES.NEW_TASK]: {
        id: "new-task",
        steps: [
            {
                name: "Step1",
                match: NewTaskSteps.matchStep1,
                run: NewTaskSteps.runStep1
            }
        ]
    }
};

Adding New Steps

  1. Add Step Type:
const STEP_TYPES = {
    NEW_TASK: {
        // ... existing steps
        NEW_STEP: 'NewStep'
    }
};
  1. Implement Step Functions:
const NewTaskSteps = {
    // ... existing steps
    matchNewStep: (url, ctx) => {
        return url.includes("/target-page") && ctx?.step_data?.previous_step_completed;
    },
    
    runNewStep: async (ctx) => {
        await TL.guardDoubleRun(ctx, async () => {
            // Step logic here
            ctx.step_data.new_step_completed = true;
        });
    }
};
  1. Update Task Definition:
[TASK_TYPES.NEW_TASK]: {
    id: "new-task",
    steps: [
        // ... existing steps
        {
            name: "NewStep",
            match: NewTaskSteps.matchNewStep,
            run: NewTaskSteps.runNewStep
        }
    ]
}

Best Practices

  1. Always use guardDoubleRun for step functions to prevent double execution
  2. Use enums for type safety (TASK_TYPES, LOGIN_METHOD, etc.)
  3. Validate task data using TaskRunner.validateTaskData
  4. Handle navigation using ctx.goto() instead of direct window.location
  5. Use context helpers (ctx.wait(), ctx.retry()) for robust execution
  6. Log appropriately using TL.log(), TL.debug(), TL.error()
  7. Update step_data to track progress and avoid duplicate work
  8. Handle errors gracefully with try-catch and proper error reporting

Debugging Tips

  1. Enable debug mode: Set CONFIG.is_debug = true
  2. Check navigation state: Use STORAGE.getNavigationState()
  3. Monitor state changes: Watch APP_STATE.getData() values
  4. Check step execution: Look for [STEP] logs in console
  5. Verify page detection: Use BINANCE.detectPage() to check current page
  6. Test step matching: Use step match functions directly
  7. Check task data: Use TaskRunner.getCurrentTaskData()
Description
No description provided
Readme 220 KiB
Languages
JavaScript 100%