// SPDX-License-Identifier: MIT

pragma solidity >=0.6.9 <0.8.0;

import "../../lib/ERC1271.sol";
import "./LibOrder.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

abstract contract OrderValidator is Initializable, ContextUpgradeable, ReentrancyGuardUpgradeable, EIP712Upgradeable {
    using ECDSAUpgradeable for bytes32;
    using AddressUpgradeable for address;
    
    // bytes4(keccak256("isValidSignature(bytes32,bytes)")
    bytes4 constant internal MAGICVALUE = 0x1626ba7e;

    function __OrderValidator_init_unchained() internal initializer {
        __EIP712_init_unchained("Exchange", "2");
    }

    function getChainId() external view returns (uint256 chainId) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        // solhint-disable-next-line no-inline-assembly
        assembly {
            chainId := chainid()
        }
    }

    function validate(LibOrder.Order memory order, bytes memory signature) internal view {
        if (_msgSender() != order.maker) {
            bytes32 hash = LibOrder.hash(order);
            if (order.maker.isContract()) {
                require(
                    ERC1271(order.maker).isValidSignature(_hashTypedDataV4(hash), signature) == MAGICVALUE,
                    "signature verification error"
                );
            } else {
                require(
                    _hashTypedDataV4(hash).recover(signature) == order.maker,
                    "signature verification error"
                );
            }
        }
    }

    uint256[50] private __gap;
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"Cancel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"leftHash","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"rightHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"leftMaker","type":"address"},{"indexed":true,"internalType":"address","name":"rightMaker","type":"address"},{"indexed":false,"internalType":"uint256","name":"newLeftFill","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRightFill","type":"uint256"}],"name":"Match","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"assetType","type":"bytes4"},{"indexed":false,"internalType":"address","name":"matcher","type":"address"}],"name":"MatcherChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"assetType","type":"bytes4"},{"indexed":false,"internalType":"address","name":"proxy","type":"address"}],"name":"ProxyChange","type":"event"},{"anonymous":false,"inputs":[{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"indexed":false,"internalType":"struct LibAsset.Asset","name":"asset","type":"tuple"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"bytes4","name":"transferDirection","type":"bytes4"},{"indexed":false,"internalType":"bytes4","name":"transferType","type":"bytes4"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"contract TransferProxy","name":"_transferProxy","type":"address"},{"internalType":"contract ERC20TransferProxy","name":"_erc20TransferProxy","type":"address"},{"internalType":"uint256","name":"newProtocolFee","type":"uint256"},{"internalType":"address","name":"newCommunityWallet","type":"address"},{"internalType":"contract IRoyaltiesProvider","name":"newRoyaltiesProvider","type":"address"}],"name":"__ExchangeV2_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"makeAsset","type":"tuple"},{"internalType":"address","name":"taker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"takeAsset","type":"tuple"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"bytes4","name":"dataType","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibOrder.Order","name":"order","type":"tuple"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"communityWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint96","name":"value","type":"uint96"}],"internalType":"struct LibPart.Part[]","name":"payouts","type":"tuple[]"},{"components":[{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint96","name":"value","type":"uint96"}],"internalType":"struct LibPart.Part[]","name":"originFees","type":"tuple[]"}],"internalType":"struct LibOrderDataV1.DataV1","name":"data","type":"tuple"}],"name":"encodeDataV1","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"fills","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"makeAsset","type":"tuple"},{"internalType":"address","name":"taker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"takeAsset","type":"tuple"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"bytes4","name":"dataType","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibOrder.Order","name":"orderLeft","type":"tuple"},{"internalType":"bytes","name":"signatureLeft","type":"bytes"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"makeAsset","type":"tuple"},{"internalType":"address","name":"taker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"takeAsset","type":"tuple"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"bytes4","name":"dataType","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibOrder.Order","name":"orderRight","type":"tuple"},{"internalType":"bytes","name":"signatureRight","type":"bytes"}],"name":"matchOrders","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"makeAsset","type":"tuple"},{"internalType":"address","name":"taker","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"assetClass","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibAsset.AssetType","name":"assetType","type":"tuple"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LibAsset.Asset","name":"takeAsset","type":"tuple"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"bytes4","name":"dataType","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct LibOrder.Order","name":"order","type":"tuple"}],"name":"orderHashKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"royaltiesRegistry","outputs":[{"internalType":"contract IRoyaltiesProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"setAllowedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"assetType","type":"bytes4"},{"internalType":"address","name":"matcher","type":"address"}],"name":"setAssetMatcher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newCommunityWallet","type":"address"}],"name":"setCommunityWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newProtocolFee","type":"uint256"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"assetType","type":"bytes4"},{"internalType":"address","name":"proxy","type":"address"}],"name":"setTransferProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"wallet","type":"address"}],"name":"setWalletForToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"walletsForTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]