I cant get correct recovered address with ECDSA.recover

I tried everything and I dont know what I am doing wrong.

I have this function in smart contract:

function test(
        bytes memory signature,
        address to
    ) public pure returns (address) {
        // VERIFY THE SIGNATURE
        bytes32 messageHash = keccak256(abi.encodePacked(to));
        address recoveredAddress = ECDSA.recover(messageHash, signature);

        return recoveredAddress;

        //require(recoveredAddress == msg.sender, "Invalid signature");
    }

And I have this HardHat test

const { SContract, mockToken, owner, addr1, addr2 } = await loadFixture(deployFixture);
    
            const amount = BigNumber.from("100");
            const fee = amount.mul(5).div(100);
            const donationAmount = amount.sub(fee);
    
            // sign a message with the donor's address
            const addr2Lower = (addr2.address).toLowerCase();

            const messageHash = ethers.utils.solidityKeccak256(["string"], [addr2Lower]);
            const messageHashBinary = ethers.utils.arrayify(messageHash);
            const signature = await addr1.signMessage(messageHashBinary);

            console.log("Addr2: " + addr2Lower);
            console.log("Signature: " + signature);

            const recovered = await SContract.connect(addr1).test(signature, addr2Lower);
            console.log("Signer: ", (addr1.address).toLowerCase());
            console.log("Recovered: ", recovered);

Result is:
Signer: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8
Recovered: 0x2c78E0b3a60425283f3Ee056d00A08560A445d9d

What I am doing wrong?

You're hashing to and then verifying against msg.sender.

Why?

require(recoveredAddress == msg.sender, "Invalid signature"); was commented with //, so this doesnt matter.

Anyway, I changed my code to this:

function test(
        bytes memory signature,
        address from,
        address to
    ) public view returns (bool) {
        // VERIFY THE SIGNATURE
        bytes32 messageHash = keccak256(abi.encodePacked(from, to));
        bytes32 message = ECDSA.toEthSignedMessageHash(messageHash);
        address recoveredAddress = ECDSA.recover(message, signature);

        require(recoveredAddress == msg.sender, "Invalid signature");

        return true;
    }

I added this part: bytes32 message = ECDSA.toEthSignedMessageHash(messageHash);

In my test code:

const messageHash = ethers.utils.solidityKeccak256(["address","address"], [addr1.address,addr2.address])
const messageHashBinary = ethers.utils.arrayify(messageHash)
const signature = await addr1.signMessage(messageHashBinary);

Now its working I guess. Is this safe?

Yes you should be using toEthSignedMessageHash if you're using the signMessage method.