Разработка Multicall-контрактов (batch операции)

Читать состояние десяти контрактов по отдельности — это десять RPC-запросов, десять round-trip-ов до ноды. На публичных эндпоинтах загрузка dApp растягивается на 2–5 секунд. Batch-операции Multicall решают проблему: на чтение — агрегация запросов в один RPC вызов, на запись — несколько on-chain опер

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1450
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1003
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1269
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    717
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1008

Читать состояние десяти контрактов по отдельности — это десять RPC-запросов, десять round-trip-ов до ноды. На публичных эндпоинтах загрузка dApp растягивается на 2–5 секунд. Batch-операции Multicall решают проблему: на чтение — агрегация запросов в один RPC вызов, на запись — несколько on-chain операций в одной транзакции. Нагрузка на ноду снижается на 90%, пользовательский опыт становится отзывчивым.

Наш опыт — 5+ лет в Web3, мы развернули более 20 смарт-контрактов для DeFi-протоколов, NFT-маркетплейсов и кросс-чейн мостов. Все контракты проходят аудит безопасности, гарантируем соответствие лучшим практикам.

Как Multicall3 снижает RPC-нагрузку?

Multicall3 на адресе 0xcA11bde05977b3631167028862bE2a173976CA11 задеплоен на 50+ сетях. Принимает массив (address target, bytes callData), исполняет все call-ы и возвращает результаты. Для read-only запросов используется eth_call, при этом все вызовы выполняются за один RPC-запрос.

Типичное использование через wagmi/viem:

import { useReadContracts } from 'wagmi'; const { data } = useReadContracts({ contracts: [ { address: token1, abi: erc20Abi, functionName: 'balanceOf', args: [user] }, { address: token2, abi: erc20Abi, functionName: 'balanceOf', args: [user] }, { address: pool, abi: poolAbi, functionName: 'getReserves' }, { address: oracle, abi: oracleAbi, functionName: 'latestAnswer' }, ] }); // один RPC запрос вместо четырёх 

Wagmi использует Multicall3 под капотом: все вызовы в одном useReadContracts схлопываются в один eth_call. Если сеть не поддерживает Multicall3, wagmi fallback-ает к параллельным eth_call, что всё равно экономит время. Экономия газа до 70% в batch-операциях.

Когда кастомный контракт выигрывает?

Multicall3 не хранит состояние, не проверяет авторизацию, не поддерживает нативный ETH на отдельные вызовы. Для write-операций с кастомной логикой пишем собственный контракт.

Паттерн self-multicall — контракт вызывает сам себя через несколько функций в одной транзакции. OpenZeppelin Multicall реализует это через Multicall.sol:

abstract contract Multicall { function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { (bool success, bytes memory result) = address(this).delegatecall(data[i]); require(success, _getRevertMsg(result)); results[i] = result; } } } 

delegatecall на address(this) — контракт вызывает свои функции от имени оригинального msg.sender. Это позволяет делать approve + deposit в одном вызове, где оба шага видят одинаковый msg.sender.

Критическое предупреждение по безопасности

delegatecall на address(this) с пользовательским data — потенциальная уязвимость. Если контракт не изолирован от привилегированных функций, атакующий может составить data, вызывающий transferOwnership. OpenZeppelin предупреждает: не используйте этот паттерн с контрактами, где авторизация зависит от msg.sender, без дополнительных проверок. Мы при разработке обязательно применяем проверки прав доступа на каждом шаге: каждая функция, доступная через self-multicall, проверяет msg.sender.

Как реализовать атомарные multi-step операции?

Для сложных DeFi операций (flash loan → swap → repay) нужен контракт с промежуточным состоянием, который откатывает всё при ошибке любого шага:

contract AtomicBatcher { struct Step { address target; bytes callData; uint256 value; uint256 minReturnValue; // проверка результата } function executeBatch(Step[] calldata steps) external payable returns (bytes[] memory results) { results = new bytes[](steps.length); for (uint256 i = 0; i < steps.length; i++) { (bool success, bytes memory result) = steps[i].target.call{ value: steps[i].value }(steps[i].callData); require(success, string(abi.encodePacked("Step ", i, " failed"))); if (steps[i].minReturnValue > 0) { uint256 returnValue = abi.decode(result, (uint256)); require(returnValue >= steps[i].minReturnValue, "Slippage exceeded"); } results[i] = result; } // вернуть незатраченный ETH if (address(this).balance > 0) { (bool sent,) = msg.sender.call{value: address(this).balance}(""); require(sent); } } } 

minReturnValue — встроенная защита от slippage на каждом шаге. Swap вернул меньше минимума — вся транзакция откатывается.

Сравнение Multicall3 и кастомного контракта

Сценарий Multicall3 Кастомный
Агрегация read-запросов Достаточно Избыточно
Несколько ERC-20 transfer Достаточно Избыточно
Approve + protocol action (один токен) Достаточно Достаточно
Flash loan + arbitrage + repay Не подходит Нужен
Условные действия (если результат шага X > Y) Не подходит Нужен
Распределение ETH по адресам с разными суммами Не подходит Нужен

Оценка длительности разработки

Сложность Примерная длительность Gas-оптимизация
Простая (read aggregation) 1 день Достаточно Multicall3
Средняя (write batch с проверками) 2-3 дня Частично кастомная
Сложная (multi-step DeFi) 3-5 дней Полностью кастомная

Что входит в работу

  • Анализ требований и проектирование архитектуры.
  • Написание и тестирование Solidity-контракта (Foundry, Tenderly).
  • Аудит безопасности (Slither, Mythril, ручной review).
  • Деплой и верификация кода на Etherscan.
  • Документация (Natspec, README) и доступ к репозиторию.
  • 1 месяц инцидент-менеджмента и поддержки.
  • Обучение команды заказчика работе с контрактом.

Пошаговый процесс разработки

  1. Анализ требований — определяем сценарии использования, требования к безопасности и gas-лимиты.
  2. Проектирование архитектуры — выбираем между Multicall3 и кастомным контрактом, проектируем структуру данных.
  3. Написание кода — реализуем Solidity-контракт с учётом gas optimization и проверок.
  4. Тестирование — модульные тесты на Foundry, стресс-тесты на Tenderly, симуляция.
  5. Аудит безопасности — статический анализ (Slither, Mythril), ручной review опытными разработчиками.
  6. Деплой и верификация — развёртывание в целевой сети, верификация кода на Etherscan.
  7. Документация и поддержка — Natspec, README, 1 месяц инцидент-менеджмента.

Закажите разработку — наши инженеры оценят ваш проект за один день. Получите консультацию в Telegram или по почте. Опыт — 5+ лет, более 20 успешных запусков. Гарантируем чистоту кода и прохождение внешнего аудита.