Разработка Account Abstraction Bundler

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка Account Abstraction Bundler
Сложная
~1-2 недели
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1258
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1092
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830

Разработка Account Abstraction Bundler

Bundler — центральный инфраструктурный компонент ERC-4337 экосистемы. Именно он обеспечивает работу Account Abstraction: принимает UserOperation от пользователей, валидирует их, группирует в batch и отправляет on-chain через EntryPoint.handleOps(). Без bundler-а Account Abstraction не работает — нет механизма доставки UserOps в блокчейн.

Разработка собственного bundler-а актуальна если: нужна кастомная мемпул логика, MEV оптимизация для UserOps, приватный bundler для конкретного приложения, или если требуется понимать и контролировать всю инфраструктуру ERC-4337.

Что делает Bundler: детальный флоу

Пользователь создаёт UserOperation и отправляет её в bundler через JSON-RPC метод eth_sendUserOperation. Bundler выполняет серию проверок, держит UserOp в своём alt mempool, и периодически отправляет batch on-chain.

Фаза 1: Validation

Bundler вызывает EntryPoint.simulateValidation(userOp). Это view-функция (reverts с результатом через custom error), которая:

  1. Если initCode не пустой — деплоит Account контракт через factory
  2. Вызывает account.validateUserOp() — проверяет подпись, nonce
  3. Если указан Paymaster — вызывает paymaster.validatePaymasterUserOp()
  4. Возвращает ValidationResult с данными о газе, стейкинге paymaster-а, временных ограничениях
interface ValidationResult {
  returnInfo: {
    preOpGas: bigint;
    prefund: bigint;  // сколько ETH account/paymaster депозировал в EntryPoint
    sigFailed: boolean;
    validAfter: number;
    validUntil: number;
  };
  senderInfo: StakeInfo;
  factoryInfo?: StakeInfo;
  paymasterInfo?: StakeInfo;
}

prefund — ключевой момент. Account или Paymaster должны иметь депозит в EntryPoint достаточный для покрытия maxFeePerGas * (verificationGasLimit + callGasLimit). Bundler проверяет это до включения в mempool.

Storage Access Rules: почему это сложно

ERC-4337 накладывает жёсткие ограничения на то, какой storage может читать/писать validateUserOp. Цель — предотвратить ситуацию когда одна UserOp делает другие невалидными (griefing атака).

Запрещено в validation:

  • Читать storage других контрактов, кроме самого Account и связанных entity
  • Вызывать block.timestamp, block.number (кроме ограниченного использования через validAfter/validUntil)
  • Обращаться к storage, которое может быть изменено другой UserOp в том же batch

Bundler отслеживает storage slots, к которым обращается validation через debug_traceCall с EVM tracer. Это дорогостоящая операция — один из главных performance bottleneck-ов bundler-а.

// Упрощённо: tracer для отслеживания storage access
async function traceValidation(userOp: UserOperation): Promise<StorageMap> {
  const trace = await provider.send('debug_traceCall', [{
    to: ENTRY_POINT_ADDRESS,
    data: entryPoint.interface.encodeFunctionData('simulateValidation', [userOp])
  }, 'latest', {
    tracer: bundlerCollectorTracer, // кастомный JS tracer
    tracerConfig: { /* ... */ }
  }])
  
  return parseStorageAccess(trace)
}

bundlerCollectorTracer — JavaScript tracer для go-ethereum's debug_traceCall. Отслеживает каждый SLOAD/SSTORE opcode и связывает их с вызывающим контрактом. Это самая технически нетривиальная часть bundler-а.

Фаза 2: Alt Mempool Management

UserOp принятая в mempool должна оставаться валидной. Bundler следит за:

Nonce invalidation. Если on-chain нонс Account изменился (другая UserOp прошла) — pending UserOp с устаревшим nonce удаляется.

Deposit insufficiency. Если balance депозита в EntryPoint уменьшился (другая UserOp спонсируемая тем же Paymaster-ом прошла) — нужно пересчитать хватит ли на все pending UserOps этого Paymaster-а.

Gas price changes. UserOp с maxFeePerGas ниже текущего basefee — не пройдёт, bundler может временно отложить или дропнуть.

class UserOpMempool {
  private pool: Map<string, MempoolEntry> = new Map()
  
  async add(userOp: UserOperation): Promise<string> {
    const hash = getUserOpHash(userOp)
    
    // Репутационная система: ограничение по sender/paymaster/factory
    this.reputationManager.checkReputation(userOp)
    
    this.pool.set(hash, {
      userOp,
      prefund: await this.calculatePrefund(userOp),
      addedAt: Date.now()
    })
    
    return hash
  }
  
  getBundle(maxGas: bigint): UserOperation[] {
    // Жадный алгоритм: выбрать UserOps с наибольшим priority fee
    // с учётом лимита газа и конфликтов по storage
    return this.selectNonConflicting(
      [...this.pool.values()]
        .sort((a, b) => Number(b.userOp.maxPriorityFeePerGas - a.userOp.maxPriorityFeePerGas)),
      maxGas
    )
  }
}

Фаза 3: Bundle Submission

Bundler формирует batch из валидных UserOps и отправляет EntryPoint.handleOps(ops, beneficiary). beneficiary — адрес куда EntryPoint отправит собранный газ (priority fee bundler-а).

Критичный момент: bundler отправляет обычную EOA транзакцию. Он платит газ авансом, EntryPoint возмещает из депозитов Account/Paymaster. Если handleOps реверсируется — bundler теряет газ. Поэтому симуляция перед отправкой обязательна.

Защита от reverting bundle: EntryPoint в handleOps пропускает UserOps, которые реверсятся в execution phase (не в validation). Для validation фаза — если реверт, весь handleOps падает. Bundler должен убедиться что validation гарантированно пройдёт.

Репутационная система

Чтобы предотвратить spam и DoS атаки, ERC-4337 вводит reputation system для unbanned entities (Paymaster, Factory, Aggregator). Логика:

class ReputationManager {
  // Для каждого entity отслеживаем: ops включённых vs ops неуспешных
  
  updateIncluded(entity: string): void {
    this.entries[entity].opsSeen++
    this.entries[entity].opsIncluded++
  }
  
  updateFailed(entity: string): void {
    this.entries[entity].opsIncluded-- // если bundle был reverted
  }
  
  getStatus(entity: string): 'ok' | 'throttled' | 'banned' {
    const entry = this.entries[entity]
    if (!entry) return 'ok'
    
    const ratio = entry.opsIncluded / Math.max(1, entry.opsSeen)
    if (ratio < MIN_INCLUSION_RATE_DENOMINATOR) return 'banned'
    if (entry.opsSeen > THROTTLE_THRESHOLD) return 'throttled'
    return 'ok'
  }
}

Staking в EntryPoint повышает лимиты: entity со стейком может иметь больше UserOps в mempool. Это anti-spam механизм: нельзя бесплатно флудить mempool.

P2P Mempool

Для децентрализованного bundler-а нужен P2P alt mempool — сеть для обмена UserOps между bundler нодами. ERC-4337 специфицирует протокол на базе libp2p с gossipsub:

  • Topic: user_ops/{chainId}/{entryPointAddress}
  • Message: RLP-encoded UserOperation
  • Validation: каждый узел независимо валидирует перед relay
import { createLibp2p } from 'libp2p'
import { gossipsub } from '@chainsafe/libp2p-gossipsub'

const libp2p = await createLibp2p({
  /* ... transport, identify, etc */
  services: {
    pubsub: gossipsub({
      allowPublishToZeroPeers: true,
      msgIdFn: (msg) => computeUserOpHash(msg.data)
    })
  }
})

libp2p.services.pubsub.subscribe(userOpsTopic)
libp2p.services.pubsub.addEventListener('message', async (event) => {
  const userOp = decodeUserOp(event.detail.data)
  await mempool.add(userOp) // со всеми проверками
})

MEV и Bundle строительство

Bundler имеет уникальную позицию: он выбирает порядок UserOps в bundle, что открывает MEV возможности. Две стратегии:

Честный FIFO bundler — включает UserOps в порядке получения, максимизирует priority fee. Простая реализация, хорошо для permissioned bundler конкретного приложения.

MEV-aware bundler — анализирует callData UserOps, находит арбитражные возможности, строит bundle оптимально. Интеграция с flashbots MEV-boost для отправки bundle через private mempool.

Имплементации для изучения и форка

  • Infinitism/bundler (TypeScript) — reference implementation от создателей ERC-4337
  • Stackup bundler (Go) — production bundler от Stackup
  • Silius (Rust) — high-performance bundler
  • Rundler (Rust) — bundler от Alchemy

Для кастомной разработки: TypeScript reference проще для понимания, Rust/Go лучше для production throughput.

Стек разработки

Компонент Технология
RPC сервер Node.js / Go / Rust
EVM tracing debug_traceCall + кастомный JS tracer
Mempool storage Redis / in-memory + persistence
P2P (опционально) libp2p + gossipsub
Мониторинг Prometheus + Grafana
Тестирование Foundry + Hardhat (local EntryPoint)

Сроки

Базовый centralized bundler с RPC, validation, mempool и bundle submission: 6-8 недель. Основная сложность — корректный EVM tracer для storage access rules.

Production bundler с репутационной системой, P2P mempool, MEV оптимизацией, мониторингом: 3-4 месяца.

Главное предупреждение: некорректная реализация storage access rules ведёт к либо принятию опасных UserOps (DoS риск), либо к отклонению валидных (плохой UX). Тщательное тестирование на всех edge cases обязательно.