Decompiled with EVMDecompiler
Published on July 3, 2025
We grabbed an NFT contract that never published its source, ran it through the decompiler, and rebuilt the classic wallet enumeration helper developers love to copy. Watching the while loop, memory allocation, and balance checks fall back into place reminds us why readable output matters for audits.
Here's what stood out while we pieced the function back together:
balanceOf and ownerOf to keep counts honest.while guard over tokenId <= maxSupply survived intact.supplyLimit for maxSupply to match how devs usually name it.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;
}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;
}You see this pattern all over NFT projects. Getting the loop bounds, array allocation, and conditional writes right is how we spot off-by-one bugs or nasty gas bombs. The regenerated version keeps the original's intent but tightens the naming (maxSupply vs supplyLimit) so reviewers don't have to squint.
while condition.new uint256[](count) sequence.ownerOf(tokenId) call and the equality checks that gate writes.