Разработка системы управления кошельками для airdrop-фарминга
Airdrop-фарминг в 2024-2025 — это профессиональная деятельность с сотнями кошельков, систематическими on-chain транзакциями и работой с десятками протоколов одновременно. Ручное управление не масштабируется: слишком много повторяющихся операций, слишком высок риск ошибок, слишком сложно отслеживать прогресс по каждому кошельку. Система автоматизации — обязательный инструмент для серьёзного фармера.
Что такое профессиональный airdrop-фарминг
Анализ крупных airdrop-ов (Arbitrum, Optimism, ZkSync, LayerZero, EigenLayer) показывает паттерны, которые награждались: регулярная активность в течение многих месяцев, разнообразие протоколов, нативные транзакции (не только bridging), держание токенов, участие в governance. Система должна автоматизировать именно эти паттерны, сохраняя органичность активности.
Архитектура системы
Иерархия кошельков
Профессиональный фармер работает с несколькими уровнями ключей:
Master wallet — холодный кошелёк (Ledger/Trezor или air-gapped machine). Хранит основной капитал. Никогда не используется напрямую в протоколах.
Fund wallets (2-5 штук) — промежуточные кошельки для распределения средств. Получают ETH/USDC от master, распределяют по farming wallets.
Farming wallets (50-500 штук) — рабочие кошельки, которые непосредственно взаимодействуют с протоколами. Каждый генерируется как отдельный HD-путь от одной или нескольких seed фраз.
Master Wallet
↓ (manual transfers)
Fund Wallets [1-5]
↓ (automated distribution)
Farming Wallets [50-500]
↓ (automated interactions)
DeFi Protocols
Важно: farming wallets не должны иметь прямых on-chain связей с master wallet. Цепочка fund wallet → farming wallet с разными временными интервалами снижает корреляцию.
Генерация и хранение ключей
Все farming wallets генерируются детерминированно из seed фраз по BIP44:
import { HDNodeWallet, Mnemonic } from "ethers";
function generateFarmingWallets(
mnemonic: string,
count: number,
startIndex: number = 0
): WalletInfo[] {
const masterNode = HDNodeWallet.fromMnemonic(
Mnemonic.fromPhrase(mnemonic)
).derivePath("m/44'/60'/0'/0");
return Array.from({ length: count }, (_, i) => {
const wallet = masterNode.deriveChild(startIndex + i);
return {
index: startIndex + i,
address: wallet.address,
privateKey: wallet.privateKey,
derivationPath: `m/44'/60'/0'/0/${startIndex + i}`,
};
});
}
Хранение. Никогда не храним private keys в открытом виде. Варианты:
- Шифрование через AES-256-GCM с паролем (KDF: Argon2id)
- Хранение только seed phrase + on-demand derivation
- HashiCorp Vault для командного использования
База данных кошельков и активности
CREATE TABLE wallets (
id SERIAL PRIMARY KEY,
address VARCHAR(42) UNIQUE NOT NULL,
derivation_path VARCHAR(64),
wallet_group VARCHAR(64), -- для группировки
created_at TIMESTAMPTZ DEFAULT NOW(),
last_active_at TIMESTAMPTZ,
total_gas_spent NUMERIC(30, 18) DEFAULT 0,
notes TEXT,
tags TEXT[]
);
CREATE TABLE protocol_interactions (
id BIGSERIAL PRIMARY KEY,
wallet_id INTEGER REFERENCES wallets(id),
protocol VARCHAR(128) NOT NULL,
chain_id INTEGER NOT NULL,
tx_hash VARCHAR(66),
action_type VARCHAR(64), -- swap, bridge, stake, lp, vote
amount NUMERIC(30, 18),
gas_used NUMERIC(30, 18),
executed_at TIMESTAMPTZ DEFAULT NOW(),
status VARCHAR(16) DEFAULT 'pending',
metadata JSONB
);
CREATE TABLE farming_tasks (
id BIGSERIAL PRIMARY KEY,
wallet_id INTEGER REFERENCES wallets(id),
task_type VARCHAR(128) NOT NULL,
protocol VARCHAR(128) NOT NULL,
chain_id INTEGER NOT NULL,
parameters JSONB NOT NULL,
scheduled_at TIMESTAMPTZ,
executed_at TIMESTAMPTZ,
status VARCHAR(16) DEFAULT 'pending',
retry_count INTEGER DEFAULT 0,
error_message TEXT
);
Автоматизация взаимодействий
Task Runner
Система выполнения задач должна имитировать человеческое поведение: случайные задержки между транзакциями, вариативное время суток, разные gas prices.
class FarmingTaskRunner {
async executeTask(task: FarmingTask): Promise<TxReceipt> {
const wallet = await this.walletManager.getWallet(task.walletId);
const provider = this.getProvider(task.chainId);
// Случайная задержка 30с - 5 мин перед транзакцией
const delay = randomBetween(30_000, 300_000);
await sleep(delay);
// Случайное изменение gas price в пределах ±10%
const gasPrice = await this.getGasWithVariance(provider, 0.1);
const handler = this.handlers.get(task.taskType);
if (!handler) throw new Error(`Unknown task type: ${task.taskType}`);
return handler.execute(wallet, task.parameters, { gasPrice });
}
private async getGasWithVariance(provider: Provider, variance: number) {
const feeData = await provider.getFeeData();
const base = feeData.maxFeePerGas!;
const multiplier = 1 + (Math.random() * 2 - 1) * variance;
return base * BigInt(Math.round(multiplier * 100)) / 100n;
}
}
Protocol Handlers
Для каждого протокола — отдельный handler:
// Пример: Uniswap V3 swap handler
class UniswapV3SwapHandler implements ProtocolHandler {
async execute(
wallet: Wallet,
params: SwapParams,
options: ExecutionOptions
): Promise<TxReceipt> {
const router = new Contract(UNISWAP_V3_ROUTER, ROUTER_ABI, wallet);
const deadline = Math.floor(Date.now() / 1000) + 1800; // 30 мин
const tx = await router.exactInputSingle({
tokenIn: params.tokenIn,
tokenOut: params.tokenOut,
fee: params.fee, // 500, 3000, 10000
recipient: wallet.address,
deadline,
amountIn: params.amountIn,
amountOutMinimum: params.minAmountOut,
sqrtPriceLimitX96: 0,
}, {
maxFeePerGas: options.gasPrice,
maxPriorityFeePerGas: options.maxPriorityFeePerGas,
});
return tx.wait();
}
}
Аналогичные handlers создаются для: Curve, AAVE, GMX, Stargate, Wormhole/LayerZero bridge, Pendle, различных lending протоколов.
Планировщик задач
Задачи планируются с учётом: минимального интервала между транзакциями на одном кошельке, временных окон активности, зависимостей (сначала получить токены, потом stake).
class TaskScheduler {
async scheduleForWallet(walletId: number, plan: FarmingPlan) {
const tasks: ScheduledTask[] = [];
let currentTime = plan.startTime;
for (const action of plan.actions) {
// Случайный сдвиг до ±20% от planned interval
const variance = action.interval * 0.2;
const jitter = (Math.random() * 2 - 1) * variance;
currentTime = new Date(currentTime.getTime() + action.interval + jitter);
tasks.push({
walletId,
taskType: action.type,
protocol: action.protocol,
chainId: action.chainId,
parameters: action.params,
scheduledAt: currentTime,
});
}
await this.db.farmingTasks.bulkInsert(tasks);
}
}
Мониторинг и аналитика
Dashboard метрик
Для каждого кошелька система должна показывать:
- On-chain активность по протоколам (с датами первой/последней транзакции)
- Потраченный gas (в USD)
- Текущие позиции (LP, стейкинг, lending)
- Score по известным метрикам (объём, количество транзакций, уникальные протоколы, дни активности)
- Текущий баланс по цепям
interface WalletStats {
address: string;
totalTxCount: number;
activeDays: number; // уникальных дней с транзакциями
uniqueProtocols: number;
totalVolumeUSD: number;
totalGasSpentUSD: number;
chainActivity: Record<number, ChainActivity>;
protocolActivity: ProtocolActivity[];
estimatedAirdropScore: Record<string, number>; // project -> estimated score
}
Оценка airdrop eligibility
Для каждого отслеживаемого проекта — набор on-chain критериев с весами. Система автоматически рассчитывает текущий score каждого кошелька и выдаёт рекомендации что нужно сделать ещё.
Anti-sybil защита
Современные airdrop-системы (EigenLayer, Wormhole) активно борются с sybil-атаками. Система должна учитывать:
Уникальность активности. Не копировать паттерны между кошельками. Разные суммы, разные протоколы, разные временные паттерны.
IP rotaion. Каждый кошелёк или группа кошельков работает через отдельный proxy/VPN. Общий IP = сильный sybil-сигнал.
Source of funds. Цепочка финансирования не должна прослеживаться к одному источнику. CEX withdrawals на разные кошельки — хорошо. Прямой перевод с одного кошелька на 100 — плохо.
Age of wallet. Старые кошельки ценятся больше. Система должна создавать кошельки заблаговременно и давать им «историю» до целевого дедлайна.
Газ и экономика
При работе с 200 кошельками, 3-5 транзакциями в день на кошелёк — важна газовая оптимизация:
- Транзакции на L2 (Arbitrum, Base, Optimism) в 10-50x дешевле mainnet
- Batching: если протокол поддерживает multicall — объединять операции
- Gas price monitoring: транзакции в периоды низкого gas (ночь UTC)
- Автоматический расчёт минимального баланса ETH на каждом кошельке
Стек
| Компонент | Технология |
|---|---|
| Backend | Node.js + TypeScript, Fastify |
| Task queue | Bull + Redis |
| Database | PostgreSQL + TimescaleDB |
| Blockchain | ethers.js v6, viem |
| RPC | Alchemy, Infura (с failover) |
| Proxy | SOCKS5 rotation (Bright Data, Oxylabs) |
| Frontend | React + TanStack Query |
| Monitoring | Grafana + Prometheus |
Сроки
- MVP (генерация кошельков, базовые handlers для 3-5 протоколов, dashboard): 4-6 недель
- Production (20+ handlers, планировщик, analytics, anti-sybil): 10-14 недель
- Поддержка и обновления под новые протоколы: ongoing







