Когда три транзакции — дорогая ошибка
Пользователь хочет добавить ликвидность в пул Uniswap V3. Реальный процесс: approve токена A, approve токена B, mint позиции. Три отдельные транзакции — три подтверждения в MetaMask, три оплаты газа. Если между approve и mint произойдёт фронтран, позиция создастся по нежелательной цене, и пользователь потеряет средства. Каждая отдельная транзакция требует оплаты газа — при batch-подходе вы платите только один раз, экономя до 46% на intrinsic cost.
Batch-транзакции решают проблему: одно подтверждение, один газ, атомарное исполнение. Наша команда разрабатывает такие решения под ключ на Solidity, Rust (Solana) и Move. Оценим ваш проект за 2 дня — свяжитесь для консультации.
Почему batch-транзакции выгоднее?
Каждая транзакция в EVM стоит минимум 21 000 gas (intrinsic cost). Batch из 5 операций в отдельных транзакциях: 5 × 21 000 = 105 000 gas только на intrinsic. Через Multicall3 — один раз 21 000 + overhead роутера (~2 000 gas) + gas на каждый вызов без intrinsic cost. Batch-транзакции в 3 раза дешевле последовательного исполнения для типовых DeFi-операций.
| Сценарий | Отдельные транзакции | Batch (Multicall3) | Экономия |
|---|---|---|---|
| 3 ERC-20 transfer | 3 × 65 000 = 195 000 | ~125 000 | ~36% |
| 5 approve + swap | 5 × 46 000 = 230 000 | ~148 000 | ~36% |
| 10 NFT mint | 10 × 120 000 = 1 200 000 | ~650 000 | ~46% |
Реальные цифры зависят от логики, но экономия 30-50% на intrinsic cost — консервативная оценка.
Как внедрить batch-транзакции: 4 шага
- Выбор подхода. Определите, достаточно ли Multicall3 или нужен кастомный BatchExecutor с whitelist-ами.
- Разработка контрактов. Напишите код на Solidity, используя Foundry или Hardhat, с тестированием на форке.
- Тестирование. Покройте сценарии реэнтранси, переполнения газа и атомарности с помощью Slither и Mythril.
- Интеграция с фронтендом. Используйте viem или ethers.js для вызова batch-функций.
Как выбрать между Multicall3 и кастомным BatchExecutor?
Router-паттерн — контракт-агрегатор, принимающий массив вызовов и исполняющий их последовательно. Самый простой вариант — Multicall3 от MakerDAO, задеплоенный на большинстве EVM-сетей по адресу 0xcA11bde05977b3631167028862bE2a173976CA11.
struct Call3 {
address target;
bool allowFailure;
bytes callData;
}
function aggregate3(Call3[] calldata calls)
external
payable
returns (Result[] memory returnData);
allowFailure: false делает весь batch атомарным — если один вызов reverts, откатывается всё. allowFailure: true позволяет продолжить при ошибке (partial execution). Проблема router-паттерна: approve токена на адрес контракта-роутера. Пользователь должен доверять роутеру, что тот не уведёт токены. Для кастомных роутеров это создаёт UX-барьер и требует аудита.
EIP-4337 (Account Abstraction) — другой уровень. Пользователь контролирует smart contract wallet, который может исполнять несколько вызовов в одной UserOperation. Approve + действие атомарны, без промежуточного доверия роутеру. Стек: Biconomy, Safe{Core} AA SDK, ZeroDev.
| Критерий | Router (Multicall3) | EIP-4337 |
|---|---|---|
| Требуется approve? | Да, на роутер | Нет (кошелёк сам одобряет) |
| Атомарность | Да (через allowFailure) | Да |
| Доверие к контракту | Высокое | Минимальное |
| Сложность интеграции | Низкая | Средняя |
| Гибкость | Средняя | Высокая |
Выбор зависит от контекста: для протокольного batch-инга — router, для wallet-уровневой автоматизации — EIP-4337.
Когда Multicall3 не хватает: кастомная batch-система
Multicall3 не принимает ETH с распределением по вызовам (только общий msg.value). Не поддерживает callback-и. Не хранит состояние между вызовами в пакете. Для сложных сценариев пишем кастомный BatchExecutor:
contract BatchExecutor {
struct BatchCall {
address target;
uint256 value;
bytes data;
bool requireSuccess;
}
function executeBatch(BatchCall[] calldata calls)
external
payable
returns (bytes[] memory results)
{
results = new bytes[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory result) = calls[i].target.call{
value: calls[i].value
}(calls[i].data);
if (calls[i].requireSuccess) {
require(success, _getRevertMsg(result));
}
results[i] = result;
}
}
}
Типичные ошибки при разработке batch-систем
- Не ограниченный whitelist target-адресов — атакующий может вызвать произвольный контракт от имени BatchExecutor и вывести токены.
- Отсутствие проверки
requireSuccessдля критических вызовов — частичный failure может оставить систему в некорректном состоянии. - Игнорирование переполнения газа (gas limit) — batch из 50+ вызовов может превысить лимит блока.
Что нужно для интеграции с frontend?
На клиентской стороне формируем список вызовов и кодируем через viem:
import { encodeFunctionData } from 'viem';
import { multicall3Abi } from './abis';
const calls = [
{
target: tokenAddress,
allowFailure: false,
callData: encodeFunctionData({
abi: erc20Abi,
functionName: 'approve',
args: [spenderAddress, amount]
})
},
{
target: protocolAddress,
allowFailure: false,
callData: encodeFunctionData({
abi: protocolAbi,
functionName: 'deposit',
args: [amount]
})
}
];
await walletClient.writeContract({
address: MULTICALL3_ADDRESS,
abi: multicall3Abi,
functionName: 'aggregate3',
args: [calls]
});
Что входит в разработку batch-системы?
- Архитектурный документ с выбором подхода (router / AA / custom)
- Смарт-контракты с тестами (Foundry/Hardhat) и отчётом Slither/Mythril
- Интеграция с frontend (wagmi/viem) и примеры кода
- Деплой в тестовую сеть и инструкция по развёртыванию
- Аудит контрактов (проверка на реэнтранси, gas-оптимизацию)
- Обучение вашей команды работе с batch-системой
- Поддержка в течение месяца после сдачи
Сроки: интеграция Multicall3 в существующий dApp — 1-2 дня. Кастомный BatchExecutor с whitelist-логикой и тестами — 3-5 дней. Полноценная система с EIP-4337 и фронтендом — от 2 недель.
Получите консультацию по внедрению batch-транзакций в ваш проект — наши инженеры оценят сложность и предложат оптимальное решение.







