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
- Always analyze before executing - Use
analyzeBatchto preview costs - Use convenience functions for simple cases -
batchAdaPaymentsis simpler thanBatchBuilderfor payments - Enable auto-optimization - Let the builder optimize operation order
- Handle errors gracefully - Set
continueOnError: truefor non-critical batches - Merge similar operations - Use
mergePaymentsandmergeMintsto reduce transaction size