Skip to main content

Batch Transactions

Efficiently batch multiple operations into optimal transactions with automatic chunking and fee estimation.

Overview

The batch transaction utilities help you:

  • Combine multiple payments, mints, and burns into fewer transactions
  • Automatically split batches that exceed transaction limits
  • Estimate fees before execution
  • Optimize for minimum fees

Installation

import {
// Builder
BatchBuilder,
createBatchBuilder,
// Batch Processing
estimateBatch,
buildBatchTransactions,
// Convenience Functions
batchAdaPayments,
batchTokenTransfers,
batchMintNFTs,
batchBurnTokens,
// Optimization
groupOperationsByType,
mergePayments,
mergeMints,
splitOperationsIntoChunks,
analyzeBatch,
optimizeBatch,
calculateBatchCost,
validateBatch,
// Constants
DEFAULT_BATCH_CONFIG,
OPERATION_SIZE_ESTIMATES,
MAX_ASSETS_PER_OUTPUT,
// Types
type BatchOperation,
type BatchEstimate,
type BatchResult,
type BatchConfig,
} from 'cardano-devkit';

Quick Start

Simple Batch Payments

import { batchAdaPayments } from 'cardano-devkit';

const payments = [
{ address: "addr_test1...", lovelace: 5_000_000n },
{ address: "addr_test2...", lovelace: 10_000_000n },
{ address: "addr_test3...", lovelace: 3_000_000n },
// ... up to hundreds of payments
];

const results = await batchAdaPayments(lucid, payments);
console.log(`Completed in ${results.length} transactions`);

Using BatchBuilder

import { createBatchBuilder } from 'cardano-devkit';

const builder = createBatchBuilder(lucid);

// Add various operations
builder
.addPayment("addr_test1...", 5_000_000n)
.addPayment("addr_test2...", 10_000_000n)
.addMint(policyId, "NFT1", 1n, nftMetadata)
.addMint(policyId, "NFT2", 1n, nftMetadata)
.addBurn(oldPolicyId, "OldToken", 100n)
.addMetadata(674, { msg: ["Batch operation"] });

// Build and execute
const results = await builder.execute();

for (const result of results) {
console.log(`Tx ${result.txHash}: ${result.operationCount} operations`);
}

BatchBuilder API

Creating a Builder

const builder = createBatchBuilder(lucid, {
maxTxSize: 16384, // Max transaction size in bytes
maxOperationsPerTx: 50, // Max operations per transaction
autoOptimize: true, // Automatically optimize batches
continueOnError: false, // Stop on first error
});

// Or use the class directly
const builder = new BatchBuilder(lucid, config);

Adding Operations

Payments

// Single payment
builder.addPayment("addr_test1...", 5_000_000n);

// Payment with assets
builder.addPayment("addr_test1...", 2_000_000n, {
[policyId + assetName]: 100n
});

// Multiple payments
builder.addPayments([
{ address: "addr1...", lovelace: 5_000_000n },
{ address: "addr2...", lovelace: 3_000_000n },
]);

Minting

// Mint NFT
builder.addMint(policyId, "MyNFT", 1n, {
name: "My NFT",
image: "ipfs://...",
});

// Mint fungible tokens
builder.addMint(policyId, "MyToken", 1000000n);

// Batch mints
builder.addMints([
{ policyId, assetName: "NFT1", amount: 1n, metadata: {...} },
{ policyId, assetName: "NFT2", amount: 1n, metadata: {...} },
]);

Burning

// Burn tokens
builder.addBurn(policyId, "MyToken", 100n);

// Batch burns
builder.addBurns([
{ policyId, assetName: "Token1", amount: 50n },
{ policyId, assetName: "Token2", amount: 25n },
]);

Metadata

// CIP-20 message
builder.addMetadata(674, { msg: ["Hello, Cardano!"] });

// Custom metadata
builder.addMetadata(721, customNftMetadata);

Delegation

// Delegate to pool
builder.addDelegation("pool1...");

Stake Operations

// Register stake address
builder.addStakeRegistration();

// Deregister stake address
builder.addStakeDeregistration();

// Withdraw rewards
builder.addWithdrawal(rewardAddress, amount);

Building and Executing

// Build batch (returns operations grouped into transactions)
const batch = builder.build();

// Analyze before executing
const analysis = builder.analyze();
console.log("Estimated transactions:", analysis.estimatedTxCount);
console.log("Estimated total fees:", analysis.estimatedFees);

// Execute all transactions
const results = await builder.execute();

// Or build without executing
const transactions = await builder.buildTransactions();
for (const tx of transactions) {
// Sign and submit manually
const signed = await tx.sign.withWallet().complete();
const hash = await signed.submit();
}

Resetting

// Clear all operations
builder.reset();

Convenience Functions

batchAdaPayments

Send ADA to multiple recipients efficiently.

import { batchAdaPayments } from 'cardano-devkit';

const payments = [
{ address: "addr_test1...", lovelace: 5_000_000n },
{ address: "addr_test2...", lovelace: 10_000_000n },
{ address: "addr_test3...", lovelace: 3_000_000n },
];

const results = await batchAdaPayments(lucid, payments, {
maxPerTx: 30, // Max payments per transaction
});

// results: Array<{ txHash: string, payments: Payment[] }>

batchTokenTransfers

Transfer tokens to multiple recipients.

import { batchTokenTransfers } from 'cardano-devkit';

const transfers = [
{ address: "addr1...", policyId: "...", assetName: "Token", amount: 100n },
{ address: "addr2...", policyId: "...", assetName: "Token", amount: 200n },
];

const results = await batchTokenTransfers(lucid, transfers);

batchMintNFTs

Mint multiple NFTs efficiently.

import { batchMintNFTs } from 'cardano-devkit';

const nfts = [
{ name: "NFT #1", image: "ipfs://...", attributes: {...} },
{ name: "NFT #2", image: "ipfs://...", attributes: {...} },
{ name: "NFT #3", image: "ipfs://...", attributes: {...} },
];

const results = await batchMintNFTs(lucid, policyId, nfts, {
recipient: "addr_test1...", // Where to send the NFTs
});

batchBurnTokens

Burn multiple tokens efficiently.

import { batchBurnTokens } from 'cardano-devkit';

const burns = [
{ policyId: "...", assetName: "Token1", amount: 100n },
{ policyId: "...", assetName: "Token2", amount: 50n },
];

const results = await batchBurnTokens(lucid, burns);

Analysis & Optimization

analyzeBatch

Get detailed analysis of a batch before execution.

import { analyzeBatch } from 'cardano-devkit';

const batch = builder.build();
const analysis = analyzeBatch(batch);

console.log("Total operations:", analysis.operationCount);
console.log("By type:", analysis.operationsByType);
console.log("Estimated transactions:", analysis.estimatedTxCount);
console.log("Estimated fees:", analysis.estimatedFees);
console.log("Estimated size:", analysis.estimatedSize);

optimizeBatch

Optimize a batch for minimum transactions/fees.

import { optimizeBatch } from 'cardano-devkit';

const batch = builder.build();
const optimized = optimizeBatch(batch);

// optimized.operations may be reordered or merged
console.log("Original txs:", batch.estimatedTxCount);
console.log("Optimized txs:", optimized.estimatedTxCount);

validateBatch

Validate a batch before execution.

import { validateBatch } from 'cardano-devkit';

const batch = builder.build();
const validation = validateBatch(batch, lucid);

if (!validation.valid) {
console.error("Validation errors:", validation.errors);
} else {
console.log("Batch is valid");
}

estimateBatch

Estimate fees and transaction count.

import { estimateBatch } from 'cardano-devkit';

const estimate = await estimateBatch(lucid, operations);

console.log("Transactions needed:", estimate.txCount);
console.log("Total fees:", estimate.totalFees);
console.log("Average fee per tx:", estimate.avgFeePerTx);

Helper Functions

groupOperationsByType

Group operations by their type.

import { groupOperationsByType } from 'cardano-devkit';

const grouped = groupOperationsByType(operations);
// { payments: [...], mints: [...], burns: [...] }

mergePayments

Merge payments to the same address.

import { mergePayments } from 'cardano-devkit';

const payments = [
{ address: "addr1...", lovelace: 5_000_000n },
{ address: "addr1...", lovelace: 3_000_000n }, // Same address
{ address: "addr2...", lovelace: 2_000_000n },
];

const merged = mergePayments(payments);
// [{ address: "addr1...", lovelace: 8_000_000n }, { address: "addr2...", lovelace: 2_000_000n }]

mergeMints

Merge mints of the same asset.

import { mergeMints } from 'cardano-devkit';

const mints = [
{ policyId: "abc", assetName: "Token", amount: 100n },
{ policyId: "abc", assetName: "Token", amount: 50n },
];

const merged = mergeMints(mints);
// [{ policyId: "abc", assetName: "Token", amount: 150n }]

splitOperationsIntoChunks

Split operations into transaction-sized chunks.

import { splitOperationsIntoChunks } from 'cardano-devkit';

const chunks = splitOperationsIntoChunks(operations, {
maxOperationsPerChunk: 30,
maxSizePerChunk: 16000,
});

// chunks: Operation[][]

calculateBatchCost

Calculate total cost of a batch.

import { calculateBatchCost } from 'cardano-devkit';

const cost = await calculateBatchCost(lucid, batch);

console.log("Total ADA needed:", cost.totalLovelace);
console.log("Fees:", cost.fees);
console.log("Deposits:", cost.deposits);

Configuration

BatchConfig

interface BatchConfig {
maxTxSize?: number; // Max transaction size (default: 16384)
maxOperationsPerTx?: number; // Max operations per tx (default: 50)
maxAssetsPerOutput?: number; // Max assets per output (default: 60)
autoOptimize?: boolean; // Auto-optimize batches (default: true)
continueOnError?: boolean; // Continue on tx failure (default: false)
parallelSubmit?: boolean; // Submit txs in parallel (default: false)
}

DEFAULT_BATCH_CONFIG

const DEFAULT_BATCH_CONFIG: BatchConfig = {
maxTxSize: 16384,
maxOperationsPerTx: 50,
maxAssetsPerOutput: 60,
autoOptimize: true,
continueOnError: false,
parallelSubmit: false,
};

Types

BatchOperation

type BatchOperation =
| PaymentOperation
| MintOperation
| BurnOperation
| MetadataOperation
| DelegationOperation
| StakeRegistrationOperation
| StakeDeregistrationOperation
| WithdrawalOperation;

PaymentOperation

interface PaymentOperation {
type: 'payment';
address: string;
lovelace: bigint;
assets?: Record<string, bigint>;
}

MintOperation

interface MintOperation {
type: 'mint';
policyId: string;
assetName: string;
amount: bigint;
metadata?: Record<string, unknown>;
}

BurnOperation

interface BurnOperation {
type: 'burn';
policyId: string;
assetName: string;
amount: bigint;
}

BatchEstimate

interface BatchEstimate {
txCount: number;
totalFees: bigint;
avgFeePerTx: bigint;
operationCount: number;
operationsByType: Record<string, number>;
estimatedSize: number;
}

BatchResult

interface BatchResult {
txHash: string;
operationCount: number;
operations: BatchOperation[];
fee: bigint;
success: boolean;
error?: string;
}

Examples

Airdrop to Multiple Addresses

import { batchAdaPayments } from 'cardano-devkit';

// Airdrop 10 ADA to 1000 addresses
const recipients = addresses.map(address => ({
address,
lovelace: 10_000_000n,
}));

const results = await batchAdaPayments(lucid, recipients);
console.log(`Airdrop completed in ${results.length} transactions`);

Mint NFT Collection

import { createBatchBuilder } from 'cardano-devkit';

const builder = createBatchBuilder(lucid);

// Add 100 NFTs to mint
for (let i = 1; i <= 100; i++) {
builder.addMint(collectionPolicyId, `NFT_${i}`, 1n, {
name: `Collection #${i}`,
image: `ipfs://collection/${i}.png`,
attributes: generateAttributes(i),
});
}

const results = await builder.execute();
console.log(`Minted collection in ${results.length} transactions`);

Complex Multi-Operation Batch

import { createBatchBuilder, analyzeBatch } from 'cardano-devkit';

const builder = createBatchBuilder(lucid);

// Mix of operations
builder
// Payments
.addPayments(paymentList)
// Mints
.addMints(mintList)
// Burns
.addBurns(burnList)
// Metadata
.addMetadata(674, { msg: ["Batch operation completed"] })
// Delegation
.addDelegation("pool1...");

// Analyze before execution
const analysis = analyzeBatch(builder.build());
console.log(`Will create ${analysis.estimatedTxCount} transactions`);
console.log(`Estimated fees: ${analysis.estimatedFees} lovelace`);

// Execute if acceptable
if (analysis.estimatedFees < maxAcceptableFee) {
const results = await builder.execute();
console.log("Batch completed successfully");
}

Best Practices

  1. Always analyze before executing - Use analyzeBatch to preview costs
  2. Use convenience functions for simple cases - batchAdaPayments is simpler than BatchBuilder for payments
  3. Enable auto-optimization - Let the builder optimize operation order
  4. Handle errors gracefully - Set continueOnError: true for non-critical batches
  5. Merge similar operations - Use mergePayments and mergeMints to reduce transaction size