contract Messages is EternalStorage {
    struct Authorization {
        address authorizedSigner;
        uint256 expiration;
    }
    /**
     * Domain separator encoding per EIP 712.
     * keccak256(
     *     "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
     * )
     */
    bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472;

    /**
     * Validator struct type encoding per EIP 712
     * keccak256(
     *     "Authorization(address authorizedSigner,uint256 expiration)"
     * )
     */
    bytes32 private constant AUTHORIZATION_TYPEHASH = 0xe419504a688f0e6ea59c2708f49b2bbc10a2da71770bd6e1b324e39c73e7dc25;


    /**
     * Domain separator per EIP 712
     */
    // bytes32 public DOMAIN_SEPARATOR;
    function DOMAIN_SEPARATOR() public view returns(bytes32) {
        bytes32 salt = 0xf2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a558;
        return keccak256(abi.encode(
                EIP712_DOMAIN_TYPEHASH,
                keccak256("Multisender"),
                keccak256("2.0"),
                uintStorage[keccak256("chainId")],
                address(this),
                salt
            ));
    }

    /**
     * @notice Calculates authorizationHash according to EIP 712.
     * @param _authorizedSigner address of trustee
     * @param _expiration expiration date
     * @return bytes32 EIP 712 hash of _authorization.
     */
    function hash(address _authorizedSigner, uint256 _expiration) public pure returns (bytes32) {
        return keccak256(abi.encode(
                AUTHORIZATION_TYPEHASH,
                _authorizedSigner,
                _expiration
            ));
    }

    /**
     * @return the recovered address from the signature
     */
    function recoverAddress(
        bytes32 messageHash,
        bytes memory signature
    )
    public
    view
    returns (address)
    {
        bytes32 r;
        bytes32 s;
        bytes1 v;
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := mload(add(signature, 0x60))
        }
        bytes32 digest = keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR(),
                messageHash
            ));
        return ecrecover(digest, uint8(v), r, s);
    }

    function getApprover(uint256 timestamp, bytes memory signature) public view returns(address) {
        if (timestamp < now) {
            return address(0);
        }
        bytes32 messageHash = hash(msg.sender, timestamp);
        return recoverAddress(messageHash, signature);
    }


}
function DOMAIN_SEPARATOR() public view returns(bytes32) {
    bytes32 salt = 0xf2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a558;
    return keccak256(abi.encode(
            EIP712_DOMAIN_TYPEHASH,
            keccak256("Multisender"),
            keccak256("2.0"),
            uintStorage[keccak256("chainId")],
            address(this),
            salt
        ));
}

// bytes32 public DOMAIN_SEPARATOR;