ERC-721 Enumeration: Bytecode → Readable Solidity

Decompiled with EVMDecompiler

Published on July 3, 2025

Abstract

We successfully reconstructed a complete ERC-721 enumeration function from raw bytecode, demonstrating how our decompiler preserves complex control flow, memory allocation patterns, and business logic. This case study shows how bytecode-only contracts can be audited effectively, providing security researchers with the tools needed to analyze unverified smart contracts.

Original vs Decompiled

Original Implementation

function walletOfOwner(address _owner) public view returns (uint256[] memory) {
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
    uint256 currentTokenId = 1;
    uint256 ownedTokenIndex = 0;

    while (ownedTokenIndex < ownerTokenCount && currentTokenId <= supplyLimit) {
        address currentTokenOwner = ownerOf(currentTokenId);
        if (currentTokenOwner == _owner) {
            ownedTokenIds[ownedTokenIndex] = currentTokenId;
            ownedTokenIndex++;
        }
        currentTokenId++;
    }

    return ownedTokenIds;
}

Decompiled Output

function walletOfOwner(address _owner) public view returns (uint256[] memory) {
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
    uint256 currentTokenId = 1;
    uint256 ownedTokenIndex = 0;

    while (ownedTokenIndex < ownerTokenCount && currentTokenId <= maxSupply) {
        address currentTokenOwner = ownerOf(currentTokenId);
        if (currentTokenOwner == _owner) {
            ownedTokenIds[ownedTokenIndex] = currentTokenId;
            ownedTokenIndex++;
        }
        currentTokenId++;
    }

    return ownedTokenIds;
}

Why this matters

This pattern is common in NFT projects. Recovering the exact loop bounds, array allocation, and conditional writes is critical for audits because subtle deviations (off-by-one bounds, unchecked owners, or unsafe memory writes) often hide bugs or gas bombs. The output here mirrors the original’s semantics while clarifying naming (maxSupply vs supplyLimit) for readability.

Decompilation approach

Takeaways