The problem with the code above is that you're taking the address apart and converting it to a string of chars 1 hex digit at a time. In hexadecimal, there is not distinction between capital and small symbols (e.g., 0xA is equal to 0xa which in turn is equal to the decimal value of 10). All hex characters above the decimal value of 10 (non-decimal chars from a-f) are being converted into ASCII chars by incrementing their hex value with 0x57 to result in letters (a-z). And since input values like 0xa and 0xA are basically the same value, both would result in the same lowercase letter a.

The checksum details are lost in that conversion and the resulting string is all lowercase. If you wanted to keep the checksum info, you'll need to include the hex digit's index in the address into account, as the checksum algorithm is described as follows:

convert the address to hex, but if the ith digit is a letter (ie. it's one of abcdef) print it in uppercase if the 4*ith bit of the hash of the lowercase hexadecimal address is 1 () otherwise print it in lowercase.

Basically, you'll first need to convert the address to a lowercase string, then take the keccak256 hash of it and apply the algorithm above. More info about the checksum algorithm here.

Answer from Ahmed Tawfeeq on Stack Exchange
🌐
GitHub
gist.github.com › ottodevs › c43d0a8b4b891ac2da675f825b1d1dbf
Ethereum/Solidity toLower() equivalent, to transform strings to lowercase · GitHub
function _toLower(string memory str) internal pure returns (string memory) { bytes memory bStr = bytes(str); bytes memory bLower = new bytes(bStr.length); for (uint i = 0; i < bStr.length; i++) { // Uppercase character... if ((uint8(bStr[i]) ...
🌐
Cryptoguide
cryptoguide.dev › post › does-case-(upperlowercase)-matter-for-ethereum-addresses
Does case (upper/lowercase) matter for ethereum addresses? | Crypto Guide Dev
August 28, 2022 - Ethereum addresses are not case-sensitive. So 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 is equivalent to 0XC02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2. However, you will often see them written with some uppercase, some lowercase. These are known as checksum addresses.
🌐
GitHub
github.com › web3 › web3.js › issues › 1395
Address String Case Inconsistencies · Issue #1395 · web3/web3.js
February 23, 2018 - It is extremely frustrating to use contract and account addresses in maps only to find that the case of the address string is being changed internally by web3js. For example, even when I set the address on a contract to normalized string/case format, it is changed immediately to a random case!
Author   web3
🌐
Exceptionlife
exceptionlife.com › ethereum › question › 3586 › address-to-string-solidity
Loading...
ExceptionLife is one of the best place for Ethereum Development where you can discuss anything about Ethereum Development.
🌐
Solidity
docs.soliditylang.org › en › v0.5.12 › style-guide.html
Style Guide — Solidity 0.5.12 documentation
For long function declarations, it is recommended to drop each argument onto it’s own line at the same indentation level as the function body. The closing parenthesis and opening bracket should be placed on their own line as well at the same indentation level as the function declaration. ... function thisFunctionHasLotsOfArguments( address a, address b, address c, address d, address e, address f ) public { doSomething(); }
🌐
Medium
jeancvllr.medium.com › solidity-tutorial-all-about-addresses-ffcdf7efc4e7
Solidity Tutorial : all about Addresses | by Jean Cvllr | Medium
April 10, 2026 - Press enter or click to view image in full size · Introduction · What is (technically) an Ethereum Address ? Basic of Addresses in Solidity & address literals · Address vs address payable · Methods available with addresses (including call(), delegatecall() and staticcall() ) Types conversions between addresses and address payable ·
Find elsewhere
🌐
GitHub
github.com › CJ42 › All-About-Solidity › blob › master › articles › Addresses.md
All-About-Solidity/articles/Addresses.md at master · CJ42/All-About-Solidity
All wallets are supposed to accept Ethereum addresses expressed in capital or lowercase characters, without any difference in interpretation. (Since EIP-55, UpperCase addresses are used to validate a checksum) 0x001d3f1ef827552ae1114027bd3e...
Author   CJ42
🌐
Stack Exchange
ethereum.stackexchange.com › questions › 82854 › function-to-check-string-is-only-formed-by-lowercase-letters
solidity 0.6.x - Function to check string is only formed by lowercase letters - Ethereum Stack Exchange
April 27, 2020 - I'm trying to write a solidity contract that tests if a string consists of only lowercase letters. In javascript, I can do this with myString.match(/^[a-z]+$/) Is it possible to do something like t...
🌐
Etherscan
etherscan.io › contractdiffchecker
Ethereum Contract Diff Checker
The Ethereum BlockChain Explorer, API and Analytics Platform
🌐
Quora
quora.com › Is-an-Ethereum-Wallet-address-case-sensitive
Is an Ethereum Wallet address case-sensitive? - Quora
You could capitalize or lower-case any of the letters, and the address is still the same, since A and a are the same in HEX. However, a lot of wallets do a checksum to see if the address is valid, and the c...
🌐
GitHub
github.com › chinmay-farkya › solidity-notes › blob › main › README.md
solidity-notes/README.md at main · chinmay-farkya/solidity-notes
All wallets are supposed to accept Ethereum addresses expressed in capital or lowercase characters, without any difference in interpretation. (Since EIP-55, UpperCase addresses are used to validate a checksum) ...
Author   chinmay-farkya
Top answer
1 of 2
1

The memory layout can be tricky.

When you do bytes memory bytesInput = bytes(input);, you're essentially only shallow-copying the string (instantiating a bytes variable with bytes indexes pointing to the input's memory pointers).

To make a deep copy, considering using the updated code below:

function copyBytes(bytes memory _bytes) private pure returns (bytes memory) {
        bytes memory copy = new bytes(_bytes.length);
        uint256 max = _bytes.length + 31;
        for (uint256 i=32; i<=max; i+=32)
        {
            assembly { mstore(add(copy, i), mload(add(_bytes, i))) }
        }
        return copy;
    }

    function _toLowercase(string memory inputNonModifiable) public pure returns (string memory) {
        bytes memory bytesInput = copyBytes(bytes(inputNonModifiable));

        for (uint i = 0; i < bytesInput.length; i++) {
            // checks for valid ascii characters // will allow unicode after building a string library
            require (uint8(bytesInput[i]) > 31 && uint8(bytesInput[i]) < 127, "Only ASCII characters");
            // Uppercase character...
            if (uint8(bytesInput[i]) > 64 && uint8(bytesInput[i]) < 91) {
                // add 32 to make it lowercase
                bytesInput[i] = bytes1(uint8(bytesInput[i]) + 32);
            }
        }
        return string(bytesInput);
    }

This is a fixed "transform string to lower case" version for Solidity above ^0.5.0 (that won't modify the original string in the memory).


A complete working example tailored to your specific logic:

pragma solidity ^0.8.13;

contract Test {
    mapping(string => uint256) public _idOfName;
    mapping(address => uint256) public _idOfAddress;

    struct UserData {
        address indexUser;
        uint256 registrationDate;
        address defaultAddress;
        string userName;
        string userBio;
        string imgUrl;
        string[] linkedProfiles;
    }

    mapping(uint256 => UserData) public _userData;

    event UserDataUpdated(address defaultAddress, string userName, string userBio, string imgUrl, string[] linkedProfiles);


    /*constructor() {
        string[] memory linkedProfiles = new string;

        linkedProfiles[0] = "bbbb";

        linkedProfiles[1] = "aaaa";

        UserData memory initialData = UserData({
            indexUser: address(this),
            registrationDate: block.timestamp,
            defaultAddress: address(this),
            userName: "Private User",
            userBio: "This user has chosen not to index their account",
            imgUrl: "/assets/users/incognito.jpeg",
            linkedProfiles: linkedProfiles
        });

        _userData[1] = initialData;
    }*/

        function _setUserData(uint256 id, UserData memory newData) internal virtual returns (UserData memory) {
            _idOfName[_toLowercase(newData.userName)] = id;
            _idOfAddress[newData.defaultAddress] = id;

            _userData[id].indexUser = newData.indexUser;

            _userData[id].registrationDate = newData.registrationDate;
            _userData[id].defaultAddress = newData.defaultAddress;
            _userData[id].userName = newData.userName;
            _userData[id].userBio = newData.userBio;
            _userData[id].imgUrl = newData.imgUrl;
            
            for (uint256 i; i < newData.linkedProfiles.length; i++) {
                if (i >= _userData[id].linkedProfiles.length) {
                    _userData[id].linkedProfiles.push(newData.linkedProfiles[i]);
                } else {
                    _userData[id].linkedProfiles[i] = newData.linkedProfiles[i];
                }
            }

        emit UserDataUpdated(newData.defaultAddress, newData.userName, newData.userBio, newData.imgUrl, newData.linkedProfiles);
        
        return _userData[id];
    }

    function copyBytes(bytes memory _bytes) private pure returns (bytes memory) {
        bytes memory copy = new bytes(_bytes.length);
        uint256 max = _bytes.length + 31;
        for (uint256 i=32; i<=max; i+=32)
        {
            assembly { mstore(add(copy, i), mload(add(_bytes, i))) }
        }
        return copy;
    }

    function _toLowercase(string memory inputNonModifiable) public pure returns (string memory) {
        bytes memory bytesInput = copyBytes(bytes(inputNonModifiable));

        for (uint i = 0; i < bytesInput.length; i++) {
            // checks for valid ascii characters // will allow unicode after building a string library
            require (uint8(bytesInput[i]) > 31 && uint8(bytesInput[i]) < 127, "Only ASCII characters");
            // Uppercase character...
            if (uint8(bytesInput[i]) > 64 && uint8(bytesInput[i]) < 91) {
                // add 32 to make it lowercase
                bytesInput[i] = bytes1(uint8(bytesInput[i]) + 32);
            }
        }
        return string(bytesInput);
    }

    function test() external returns(UserData memory) {
        string[] memory linkedProfiles = new string;

        linkedProfiles[0] = "1234";

        linkedProfiles[1] = "5678";
        
        UserData memory newData = UserData({
            indexUser: address(0),
            registrationDate: block.timestamp,
            defaultAddress: address(0),
            userName: "Private User",
            userBio: "This user has chosen not to index their account",
            imgUrl: "/assets/users/incognito.jpeg",
            linkedProfiles: linkedProfiles
        });

        
        
        return _setUserData(1, newData);
    }
}

The event log confirms that the function now works as expected, without the extra string overwrites:

"args": {
            "0": "0x0000000000000000000000000000000000000000",
            "1": "Private User",
            "2": "This user has chosen not to index their account",
            "3": "/assets/users/incognito.jpeg",
            "4": [
                "1234",
                "5678"
            ],
            "defaultAddress": "0x0000000000000000000000000000000000000000",
            "userName": "Private User",
            "userBio": "This user has chosen not to index their account",
            "imgUrl": "/assets/users/incognito.jpeg",
            "linkedProfiles": [
                "1234",
                "5678"
            ]
        }

Finally, let's confirm that the key was transformed to lower case successfully:

2 of 2
2

As Mila has answered, the value gets modified in the outer method because it is passed to the _toLowerCase as a memory pointer so that any modifications will affect the original value. This is how the internal calls work to optimize the performance.

I would like to add, that instead of a manual memory copy, you could save a bunch of gas, if you would handle the UserData as calldata. Then passing the value to _toLowerCase will copy the value into memory natively.

function _setUserData(uint256 id, UserData calldata newData) internal virtual {
    _idOfName[_toLowercase(newData.userName)] = id;
    // ...
}

Though it could be not always possible, as the method, which calls _setUserData should have the value also from the calldata. But if you can, it is better to go this way.


There is also another native way to force memory copy, but I would not suggest doing this, just to know how things work. If you use this keyword to call the method, then it won't be an internal call anymore, the extra CALL is used and therefor the value gets copied:

function _setUserData(uint256 id, UserData memory newData) internal virtual {
    _idOfName[this._toLowercase(newData.userName)] = id;
    // ...
}
function _toLowercase(string memory input) public pure returns (string memory) {
    // ...
}
🌐
Solidity
docs.soliditylang.org › en › v0.8.9 › style-guide.html
Style Guide — Solidity 0.8.9 documentation
This guide is intended to provide coding conventions for writing solidity code.
🌐
Edureka Community
edureka.co › home › community › categories › blockchain › solidity casting address to address payable
Solidity casting address to address payable | Edureka Community
January 24, 2019 - 36388/solidity-casting-address-to-address-payable · Home · Community · Categories · Blockchain · Solidity casting address to address payable · solidity struct is not generated in java wrapper code Oct 27, 2023 · please can you send me a valid genesis.json file in my email adress : victordievi@gmail.com May 27, 2023 ·