Batch transactions system development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Batch transactions system development
Medium
from 1 business day to 3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1260
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    874
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1092
  • image_logo-advance_0.png
    B2B Advance company logo design
    563
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    830

Developing Batch Transaction System

User wants to add liquidity to a Uniswap V3 pool. In reality this is: approve token A → approve token B → mint position. Three transactions, three MetaMask confirmations, three gas payments. If front-run happens between approve and mint — position creates at undesirable price. Batch transactions solve this: one confirmation, one gas, atomic execution.

Two Approaches to Batch: Router and EIP-4337

Router pattern — aggregator contract that accepts array of calls and executes them sequentially. Simplest option — Multicall3 from MakerDAO, deployed to most EVM networks at 0xcA11bde05977b3631167028862bE2a173976CA11.

struct Call3 {
    address target;
    bool allowFailure;
    bytes callData;
}

function aggregate3(Call3[] calldata calls)
    external
    payable
    returns (Result[] memory returnData);

allowFailure: false makes entire batch atomic — if one call reverts, everything rolls back. allowFailure: true allows continuing batch on individual call error — use when partial execution is okay.

Router pattern problem: approve token to router contract address. User must trust router won't steal tokens. For custom routers this creates UX barrier and requires audit.

EIP-4337 (Account Abstraction) — different level. User controls smart contract wallet that can execute multiple calls in one UserOperation. Approve + action are atomic, without intermediate trust in router. Stack: Biconomy, Safe{Core} AA SDK, ZeroDev.

Choice depends on context: for protocol batch — router, for wallet-level automation — EIP-4337.

Gas Savings: Count Honestly

Each transaction in EVM costs minimum 21,000 gas (intrinsic cost). Batch of 5 operations in separate transactions: 5 × 21,000 = 105,000 gas only on intrinsic. Via Multicall3 — once 21,000 + router overhead (~2,000 gas) + gas for each call without intrinsic cost.

Scenario Separate transactions Batch (Multicall3) Savings
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%

Real numbers depend on logic of each call, but 30-50% savings on gas intrinsic cost is conservative estimate.

Custom Batch System: When Multicall3 Isn't Enough

Multicall3 doesn't accept ETH with distribution across calls (only common msg.value). Doesn't support callbacks. Doesn't store state between calls in batch.

For complex scenarios we write custom 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;
        }
    }
}

Critical security check: delegate target address choice to user? If contract accepts arbitrary target — attacker can call arbitrary contract on behalf of BatchExecutor. If BatchExecutor holds token approvals — this is a drain. Limit target to whitelist or verify contract doesn't hold foreign assets.

Frontend Integration via wagmi/viem

On client side we form call list and encode via 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]
});

Development timeline: integrating Multicall3 into existing dApp — 1-2 days. Custom BatchExecutor with whitelist logic and tests — 3-5 days.