eb01157804ecf497421961515f37b1415e1911ef
Binance Alpha Farm Agent
Automated trading agent for Binance Alpha Farm with robust state management and task-step architecture.
📋 Table of Contents
- System Architecture
- Code Structure & Module Order
- Task-Step System
- Bot Status Flow
- State Management
- Navigation State Management
- Helper Functions
- Configuration
- API Endpoints
- Development Notes
🏗️ 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:
- ready-for-new-tasks: Bot polls server for new tasks
- performing-tasks: Bot executes task steps
- pause-automation: Bot stops (manual or error)
- 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_type: 'prod',
server_url: 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_type: 'baf-server-type',
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
- Define Task Type:
const TASK_TYPES = {
// ... existing types
NEW_TASK: 'new-task'
};
- Create Step Functions:
const NewTaskSteps = {
matchStep1: (url, ctx) => { /* match logic */ },
runStep1: async (ctx) => {
await TL.guardDoubleRun(ctx, async () => {
// Step logic
});
}
};
- 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
- Add Step Type:
const STEP_TYPES = {
NEW_TASK: {
// ... existing steps
NEW_STEP: 'NewStep'
}
};
- 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;
});
}
};
- Update Task Definition:
[TASK_TYPES.NEW_TASK]: {
id: "new-task",
steps: [
// ... existing steps
{
name: "NewStep",
match: NewTaskSteps.matchNewStep,
run: NewTaskSteps.runNewStep
}
]
}
Best Practices
- Always use
guardDoubleRunfor step functions to prevent double execution - Use enums for type safety (TASK_TYPES, LOGIN_METHOD, etc.)
- Validate task data using
TaskRunner.validateTaskData - Handle navigation using
ctx.goto()instead of directwindow.location - Use context helpers (
ctx.wait(),ctx.retry()) for robust execution - Log appropriately using
TL.log(),TL.debug(),TL.error() - Update step_data to track progress and avoid duplicate work
- Handle errors gracefully with try-catch and proper error reporting
Debugging Tips
- Enable debug mode: Set
CONFIG.is_debug = true - Check navigation state: Use
STORAGE.getNavigationState() - Monitor state changes: Watch
APP_STATE.getData()values - Check step execution: Look for
[STEP]logs in console - Verify page detection: Use
BINANCE.detectPage()to check current page - Test step matching: Use step
matchfunctions directly - Check task data: Use
TaskRunner.getCurrentTaskData()
Description
Languages
JavaScript
100%