Batch Operation Atomicity Remediation
Overview
Related Detector: Batch Operation Atomicity
A function that processes an array of operations has to decide what happens when one element fails. Reverting the whole batch lets a single poisoned element grief everyone; silently skipping failed items causes fund loss and inconsistent state. The remediation is to pick a policy deliberately — atomic all-or-nothing, or isolated per-item with explicit success reporting — and never let a failure pass unrecorded.
Recommended Fix
Before (Vulnerable — one bad element decides for all, silently)
function batchTransfer(address[] calldata to, uint256[] calldata amt) external {
for (uint256 i; i < to.length; ++i) {
token.transfer(to[i], amt[i]); // a failing transfer reverts everything (or is ignored)
}
}
After (Fixed — isolate per item and report outcomes)
event ItemResult(uint256 indexed index, bool success);
function batchTransfer(address[] calldata to, uint256[] calldata amt) external {
require(to.length == amt.length, "Length mismatch");
for (uint256 i; i < to.length; ++i) {
try token.transfer(to[i], amt[i]) returns (bool ok) {
emit ItemResult(i, ok); // record every outcome
} catch {
emit ItemResult(i, false); // failure is explicit, not silent
}
}
}
If the batch must be atomic (e.g. an accounting invariant spans items), keep the plain revert-on-failure form and document it. The key is that the choice is intentional and observable.
Common Mistakes
- Catching failures and continuing without emitting or recording them, so callers believe every item succeeded.
- Using an atomic batch for user-supplied recipient lists, letting one hostile recipient block the whole operation.
- Leaving partial state (some items applied, some not) with no way to reconcile which succeeded.