EIP712 Signature – Troubleshooting Address Recovery in EIP712 with eth_signTypedData_v4

ecdsaecrecovereip712signaturesolidity

can someone help me to find out what is wrong with my solidity code? (I'm trying to implement usage of the signatures and EIP712)

(ECDSA and _hashTypedDataV4 are from OpenZeppelin: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/utils/cryptography/ECDSA.sol and https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/utils/cryptography/draft-EIP712.sol)

string public constant AUTHORIZER_SIGNATURE = "Authorizer(string websiteDomain,uint256 currentBlock,bytes32 uniqueToken)";
bytes32 private constant _AUTHORIZER_SIGNATURE_TYPEHASH = keccak256(abi.encodePacked(keccak256(bytes(AUTHORIZER_SIGNATURE))));

function _getDigest(string memory websiteDomain, uint256 signCurrentBlock, bytes32 uniqueToken) private view returns (bytes32) {
    return _hashTypedDataV4(
        _getStructHashFromPayloadMsg(websiteDomain, signCurrentBlock, uniqueToken)
    );
}

function _getStructHashFromPayloadMsg(string memory websiteDomain, uint256 signCurrentBlock, bytes32 uniqueToken) private pure returns (bytes32) {
    return keccak256(
        abi.encode(
            _AUTHORIZER_SIGNATURE_TYPEHASH, // "message(string websiteDomain,uint256 currentBlock,bytes32 uniqueToken)"
            keccak256(bytes(websiteDomain)),
            signCurrentBlock,
            uniqueToken
        )
    );
}

function DEBUG_recover(string memory websiteDomain, uint256 signCurrentBlock, bytes32 uniqueToken, bytes memory signature) external view returns (address) {
    return ECDSA.recover(_getDigest(websiteDomain, signCurrentBlock, uniqueToken), signature);
}

On the node side, I write something like:

web3.currentProvider.send(
  {
    method: 'eth_signTypedData_v4',
    params: ["0xb2AF24e5249479C3160b10b15eDc1192dc1171C8",{domain:{chainId:11155111,name:"my-domain",verifyingContract:"my-deployed-contract",version:"1"},message:{websiteDomain:"a-domain",currentBlock:1,uniqueToken:"0x31323334353637383930313233343536373839"},types:{EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}],Authorizer:[{name:"websiteDomain",type:"string"},{name:"currentBlock",type:"uint256"},{name:"uniqueToken",type:"bytes32"}]},primaryType:"Authorizer"}];,
    from: '0xb2AF24e5249479C3160b10b15eDc1192dc1171C8',
  },
  (err, res) => {
    if (err) {
      console.error(err);
    } else {
      console.log(res);
    }
  }
);

await instance.DEBUG_recover('a-domain', 1, '0x31323334353637383930313233343536373839', '0x6b10a4e4010eec912205e604dafcff30a4204dd50f158ffd24d3f95a5b85dd2a2db3af256aac3a04a06c4e45eb606bfc3f3b7a4e0be24b796794a675f75c11411b');

So basically, I expect DEBUG_recover to return me: 0xb2AF24e5249479C3160b10b15eDc1192dc1171C8 (a testnet wallet address), but I receive another address instead…

(first I was using Ganache, but it seems to mess with the chainId… So I try with Sepolia instead, but it doesn't work either…)

Could this be related to my attribute "uniqueToken" being of type bytes32?
(I got it's "0x31323334353637383930313233343536373839" value with a command like web3.utils.asciiToHex("<another-value>")

Best Answer

Ok, so actually the problem was that I used an incorrect signature first, then when I try with string and bytes instead of bytes32 for the uniqueToken, I got another problem due to the dynamic types I guess...

I solved by problems just using correctly a bytes32 (correctly generated), so no more than 1 dynamic types value and it's now working as I expected.