Skip to main content
Sigvex

ERC-1155 Standard Violations Remediation

Validate batch array lengths, invoke receiver callbacks, and declare ERC-165 support to make multi-token contracts standard-compliant.

ERC-1155 Standard Violations Remediation

Overview

ERC-1155 compliance failures cluster in three places: batch operations that skip the parallel-array length check, safe transfers that never call the receiver callbacks, and missing ERC-165 declarations that break integration discovery. The first is the most dangerous — mismatched ids[]/amounts[] arrays corrupt transfers — and the second permanently locks tokens sent to non-receiver contracts. The reliable remediation for all of them is the same: inherit an audited implementation rather than hand-rolling the standard.

Related Detector: ERC-1155 Standard Violations

Before (Vulnerable)

contract MultiToken {
    mapping(uint256 => mapping(address => uint256)) public balances;

    function safeBatchTransferFrom(
        address from, address to,
        uint256[] calldata ids, uint256[] calldata amounts, bytes calldata
    ) external {
        // No length check, no zero-address check, no receiver callback
        for (uint256 i = 0; i < ids.length; i++) {
            balances[ids[i]][from] -= amounts[i];
            balances[ids[i]][to] += amounts[i];
        }
    }
}

After (Fixed)

import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract MultiToken is ERC1155 {
    constructor() ERC1155("https://token.example/{id}.json") {}
    // Inherited safeBatchTransferFrom validates array lengths and the
    // zero address, emits TransferBatch, and verifies
    // onERC1155BatchReceived on contract recipients.
}

The inherited implementation enforces every requirement the detector checks: ids.length == amounts.length, to != address(0), effects-before-callback ordering, correct events, and supportsInterface declarations for both IERC1155 and IERC1155MetadataURI.

Alternative Mitigations

If a custom implementation is unavoidable, apply the checks in this order inside each safe-transfer function:

  1. require(to != address(0)) and, for batches, require(ids.length == amounts.length).
  2. Update all balances (effects).
  3. Emit TransferSingle / TransferBatch.
  4. Only then invoke onERC1155Received / onERC1155BatchReceived on contract recipients and require the magic return value.

Step ordering doubles as reentrancy protection: a malicious receiver re-entering from the callback observes fully updated balances.

Common Mistakes

  • Checking the callback only in the single-transfer path. safeBatchTransferFrom needs onERC1155BatchReceived, a different selector — implementing one and forwarding to the other is a common shortcut that breaks receivers expecting the batch hook.
  • Calling the receiver before writing balances. That converts the mandated callback into a reentrancy window.
  • Skipping the length check because “our frontend always sends matching arrays.” The function is public; attackers do not use your frontend.
  • Forgetting supportsInterface. Marketplaces probe ERC-165 before listing; without it the token is compliant in behavior but invisible in practice.

References