library TypedSignature {

    // ============ Constants ============

    bytes32 constant private FILE = "TypedSignature";

    // prepended message with the length of the signed hash in decimal
    bytes constant private PREPEND_DEC = "\x19Ethereum Signed Message:\n32";

    // prepended message with the length of the signed hash in hexadecimal
    bytes constant private PREPEND_HEX = "\x19Ethereum Signed Message:\n\x20";

    // Number of bytes in a typed signature
    uint256 constant private NUM_SIGNATURE_BYTES = 66;

    // ============ Enums ============

    enum SignatureType {
        NoPrepend,
        Decimal,
        Hexadecimal,
        Invalid
    }

    // ============ Functions ============

    /**
     * Gives the address of the signer of a hash. Also allows for the commonly prepended string of
     * '\x19Ethereum Signed Message:\n' + message.length
     *
     * @param  hash               Hash that was signed (does not include prepended message)
     * @param  signatureWithType  Type and ECDSA signature with structure: {32:r}{32:s}{1:v}{1:type}
     * @return                    address of the signer of the hash
     */
    function recover(
        bytes32 hash,
        bytes memory signatureWithType
    )
        internal
        pure
        returns (address)
    {
        Require.that(
            signatureWithType.length == NUM_SIGNATURE_BYTES,
            FILE,
            "Invalid signature length"
        );

        bytes32 r;
        bytes32 s;
        uint8 v;
        uint8 rawSigType;

        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
            r := mload(add(signatureWithType, 0x20))
            s := mload(add(signatureWithType, 0x40))
            let lastSlot := mload(add(signatureWithType, 0x60))
            v := byte(0, lastSlot)
            rawSigType := byte(1, lastSlot)
        }

        Require.that(
            rawSigType < uint8(SignatureType.Invalid),
            FILE,
            "Invalid signature type"
        );

        SignatureType sigType = SignatureType(rawSigType);

        bytes32 signedHash;
        if (sigType == SignatureType.NoPrepend) {
            signedHash = hash;
        } else if (sigType == SignatureType.Decimal) {
            signedHash = keccak256(abi.encodePacked(PREPEND_DEC, hash));
        } else {
            assert(sigType == SignatureType.Hexadecimal);
            signedHash = keccak256(abi.encodePacked(PREPEND_HEX, hash));
        }

        return ecrecover(
            signedHash,
            v,
            r,
            s
        );
    }
}
/**
 * @title TypedSignature
 * @author dYdX
 *
 * Library to unparse typed signatures
 */
function recover(
    bytes32 hash,
    bytes memory signatureWithType
)
    internal
    pure
    returns (address)
{
    Require.that(
        signatureWithType.length == NUM_SIGNATURE_BYTES,
        FILE,
        "Invalid signature length"
    );

    bytes32 r;
    bytes32 s;
    uint8 v;
    uint8 rawSigType;

    /* solium-disable-next-line security/no-inline-assembly */
    assembly {
        r := mload(add(signatureWithType, 0x20))
        s := mload(add(signatureWithType, 0x40))
        let lastSlot := mload(add(signatureWithType, 0x60))
        v := byte(0, lastSlot)
        rawSigType := byte(1, lastSlot)
    }

    Require.that(
        rawSigType < uint8(SignatureType.Invalid),
        FILE,
        "Invalid signature type"
    );

    SignatureType sigType = SignatureType(rawSigType);

    bytes32 signedHash;
    if (sigType == SignatureType.NoPrepend) {
        signedHash = hash;
    } else if (sigType == SignatureType.Decimal) {
        signedHash = keccak256(abi.encodePacked(PREPEND_DEC, hash));
    } else {
        assert(sigType == SignatureType.Hexadecimal);
        signedHash = keccak256(abi.encodePacked(PREPEND_HEX, hash));
    }

    return ecrecover(
        signedHash,
        v,
        r,
        s
    );
}

/**
 * Gives the address of the signer of a hash. Also allows for the commonly prepended string of
 * '\x19Ethereum Signed Message:\n' + message.length
 *
 * @param  hash               Hash that was signed (does not include prepended message)
 * @param  signatureWithType  Type and ECDSA signature with structure: {32:r}{32:s}{1:v}{1:type}
 * @return                    address of the signer of the hash
 */