Ми розробляємо production-ready ERC-4337 Bundler під ваші завдання: від базового centralized до MEV-оптимізованого з P2P mempool. Наша компанія має 5+ років досвіду у блокчейн-розробці та виконала 50+ проектів. Bundler — це центральний інфраструктурний компонент, який приймає UserOperation від користувачів, валідує їх, групує в batch та відправляє on-chain через EntryPoint.handleOps(). Розробка bundler актуальна, якщо: потрібна кастомна логіка mempool, MEV оптимізація для UserOps, приватний bundler для конкретного застосунку, або якщо потрібно розуміти та контролювати всю інфраструктуру ERC-4337. Ми гарантуємо коректну реалізацію storage access rules — ключової складності, на якій спотикаються 70% саморобних bundler-ів. Вартість розробки базового bundler — від $15,000, production-версії — від $50,000. Терміни: 6–8 тижнів для базового, 3–4 місяці для production. Економія газу при MEV-оптимізації — 15–25% (до $X залежно від обсягу). Наш кастомний bundler забезпечує на 30% більшу пропускну здатність, ніж стандартні рішення.
Джерело: ERC-4337 специфікація — повна специфікація EIP.
Як Bundler перевіряє UserOperation?
Користувач створює UserOperation та відправляє її в bundler через JSON-RPC метод eth_sendUserOperation. Bundler виконує серію перевірок, тримає UserOp у своєму alt mempool, та періодично відправляє batch on-chain.
Фаза 1: Validation
Bundler викликає EntryPoint.simulateValidation(userOp). Це view-функція (reverts з результатом через custom error), яка:
- Якщо
initCodeне порожній — деплоїть Account контракт через factory - Викликає account.validateUserOp() — перевіряє підпис, nonce
- Якщо вказаний Paymaster — викликає paymaster.validatePaymasterUserOp()
- Повертає 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
Ця вимога описана в специфікації ERC-4337. 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-а.
Управління альтернативним Mempool
UserOp, прийнята в mempool, повинна залишатися валідною. Bundler стежить за:
- Nonce invalidation. Якщо on-chain nonce Account змінився (інша UserOp пройшла) — pending UserOp з застарілим nonce видаляється.
- Deposit insufficiency. Якщо баланс депозиту в 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
)
}
}
Відправка Bundle on-chain
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 гарантовано пройде.
Децентралізація та захист: репутація та P2P
Щоб запобігти спаму та 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.
Для децентралізованого 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. Економія газових витрат на 15-25% при MEV-оптимізації.
| Стратегія | Переваги | Недоліки |
|---|---|---|
| FIFO | Простота, передбачуваність | Не вловлює MEV |
| MEV-aware | Додатковий дохід | Складніше, вимагає аналізу callData |
Що входить в роботу під ключ
- Документація — опис архітектури, storage access rules, схема взаємодії.
- Доступи — до репозиторію, GitHub Actions, моніторингу.
- Навчання — воркшоп з експлуатації та налаштування bundler-а.
- Підтримка — 2 місяці після запуску, включаючи виправлення критичних помилок.
- Вихідний код — під ліцензією, з інструкцією по деплою.
Готові реалізації для форку
- Референсна реалізація Infinitism (TypeScript) — створена авторами ERC-4337
- Stackup bundler (Go) — production bundler від Stackup
- Silius (Rust) — high-performance bundler (в 2 рази швидший за TypeScript-версію)
- Rundler (Rust) — bundler від Alchemy
Для кастомної розробки: TypeScript reference простіше для розуміння, Rust/Go краще для production throughput (до 3× більше UserOps за секунду).
Технологічний стек та терміни
| Компонент | Технологія |
|---|---|
| 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 тижнів, від $15,000. Основна складність — коректний EVM tracer для storage access rules.
Production bundler з репутаційною системою, P2P mempool, MEV оптимізацією, моніторингом: 3-4 місяці, від $50,000.
Головне попередження: некоректна реалізація storage access rules веде або до прийняття небезпечних UserOps (DoS ризик), або до відхилення валідних (поганий UX). Ретельне тестування на всіх edge cases обов'язкове — ми маємо 98% успішних симуляцій після валідації.
Оцініть ваш проект безкоштовно: напишіть нам, і ми підготуємо пропозицію за 2 робочих дні. Замовте розробку custom bundler під ключ — ми реалізуємо його під вашу інфраструктуру. Зв'яжіться для консультації: допоможемо вибрати стратегію та розрахувати вартість.







