ERC-721 Enumeration: Bytecode → Readable Solidity

Decompiled with EVMDecompiler

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