jantektsan
jantektsan

Reputation: 11

Wagmi's signTypedDataAsync does not match with solidity's ECDSA's recover

I am getting signature by using Wagmi's signTypedDataAsync function as below.

takerOrder.signature = await signTypedDataAsync({
    types: {
        Order: [
            {name: 'salt', type: 'uint256'},
            {name: 'maker', type: 'address'},
            {name: 'signer', type: 'address'},
            {name: 'taker', type: 'address'},
            {name: 'tokenId', type: 'uint256'},
            {name: 'makerAmount', type: 'uint256'},
            {name: 'takerAmount', type: 'uint256'},
            {name: 'expiration', type: 'uint256'},
            {name: 'nonce', type: 'uint256'},
            {name: 'feeRateBps', type: 'uint256'},
            {name: 'side', type: 'uint8'},
            {name: 'signatureType', type: 'uint8'}
        ]
    },
    primaryType: 'Order',
    message: takerOrder
});

And I want to verify the signature in Solidity, but failed.

Here is OrderStruct.sol file content.

// SPDX-License-Identifier: MIT
pragma solidity <0.9.0;

bytes32 constant ORDER_TYPEHASH = keccak256(
    "Order(uint256 salt,address maker,address signer,address taker,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint256 expiration,uint256 nonce,uint256 feeRateBps,uint8 side,uint8 signatureType)"
);

struct Order {
    /// @notice Unique salt to ensure entropy
    uint256 salt;
    /// @notice Maker of the order, i.e the source of funds for the order
    address maker;
    /// @notice Signer of the order
    address signer;
    /// @notice Address of the order taker. The zero address is used to indicate a public order
    address taker;
    /// @notice Token Id of the CTF ERC1155 asset to be bought or sold
    /// If BUY, this is the tokenId of the asset to be bought, i.e the makerAssetId
    /// If SELL, this is the tokenId of the asset to be sold, i.e the takerAssetId
    uint256 tokenId;
    /// @notice Maker amount, i.e the maximum amount of tokens to be sold
    uint256 makerAmount;
    /// @notice Taker amount, i.e the minimum amount of tokens to be received
    uint256 takerAmount;
    /// @notice Timestamp after which the order is expired
    uint256 expiration;
    /// @notice Nonce used for onchain cancellations
    uint256 nonce;
    /// @notice Fee rate, in basis points, charged to the order maker, charged on proceeds
    uint256 feeRateBps;
    /// @notice The side of the order: BUY or SELL
    Side side;
    /// @notice Signature type used by the Order: EOA, POLY_PROXY or POLY_GNOSIS_SAFE
    SignatureType signatureType;
    /// @notice The order signature
    bytes signature;
}

enum SignatureType
// 0: ECDSA EIP712 signatures signed by EOAs
{
    EOA,
    // 1: EIP712 signatures signed by EOAs that own Polymarket Proxy wallets
    POLY_PROXY,
    // 2: EIP712 signatures signed by EOAs that own Polymarket Gnosis safes
    POLY_GNOSIS_SAFE
}

enum Side
// 0: buy
{
    BUY,
    // 1: sell
    SELL
}

enum MatchType
// 0: buy vs sell
{
    COMPLEMENTARY,
    // 1: both buys
    MINT,
    // 2: both sells
    MERGE
}

struct OrderStatus {
    bool isFilledOrCancelled;
    uint256 remaining;
}

And here is Hashing.sol file content.

// SPDX-License-Identifier: MIT
pragma solidity <0.9.0;

import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";

import { IHashing } from "../interfaces/IHashing.sol";

import { Order, ORDER_TYPEHASH } from "../libraries/OrderStructs.sol";

abstract contract Hashing is EIP712, IHashing {
    bytes32 public immutable domainSeparator;

    constructor(string memory name, string memory version) EIP712(name, version) {
        domainSeparator = _domainSeparatorV4();
    }

    /// @notice Computes the hash for an order
    /// @param order - The order to be hashed
    function hashOrder(Order memory order) public view override returns (bytes32) {
        return _hashTypedDataV4(
            keccak256(
                abi.encode(
                    ORDER_TYPEHASH,
                    order.salt,
                    order.maker,
                    order.signer,
                    order.taker,
                    order.tokenId,
                    order.makerAmount,
                    order.takerAmount,
                    order.expiration,
                    order.nonce,
                    order.feeRateBps,
                    order.side,
                    order.signatureType
                )
            )
        );
    }
}

Here is Verifier.sol file content.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/// @title Signatures
/// @notice Maintains logic that defines the various signature types and validates them
contract Verifier {
    /// @notice Verifies an ECDSA signature
    /// @dev Reverts if the signature length is invalid or the recovered signer is the zero address
    /// @param signer      - Address of the signer
    /// @param structHash  - The hash of the struct being verified
    /// @param signature   - The signature to be verified
    function verifyECDSASignature(address signer, bytes32 structHash, bytes memory signature)
        external
        pure
        returns (bool)
    {
        return ECDSA.recover(structHash, signature) == signer;
    }

}

After getting hash value of order by using hashOrder function, I invoked verifyECDSASignature function with order's signer and signature, and hash value as structHash, but it returns false, and signer is really not the same with ECDSA.recover(structHash, signature) result value. I really have no idea how to connect wagmi's signTypedDataAsync to ECDSA.recover. Would you please teach me how to do it?

Upvotes: 1

Views: 168

Answers (0)

Related Questions