// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @title SignatureVerifierV3
/// @notice EIP-712 + ERC-1271 verification. Uses malleability-safe ECDSA.
library SignatureVerifierV3 {
    error InvalidSignatureLength();
    error InvalidSignatureS();
    error InvalidSignatureV();
    error BadSigner();

    bytes4 internal constant EIP1271_MAGIC = 0x1626ba7e; // isValidSignature(bytes32,bytes)

    function _recover(bytes32 digest, bytes calldata sig) private pure returns (address) {
        if (sig.length != 65) revert InvalidSignatureLength();
        bytes32 r;
        bytes32 s;
        uint8 v;
        assembly {
            r := calldataload(sig.offset)
            s := calldataload(add(sig.offset, 32))
            v := byte(0, calldataload(add(sig.offset, 64)))
        }
        // Reject signatures whose s is in the upper half order (EIP-2).
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            revert InvalidSignatureS();
        }
        if (v != 27 && v != 28) revert InvalidSignatureV();
        address signer = ecrecover(digest, v, r, s);
        if (signer == address(0)) revert BadSigner();
        return signer;
    }

    /// @notice Verify a signature. Handles both EOA (EIP-712) and smart-contract
    ///         wallets (ERC-1271). `expected` is the declared signer.
    function verify(address expected, bytes32 digest, bytes calldata sig) internal view returns (bool) {
        if (expected.code.length == 0) {
            return _recover(digest, sig) == expected;
        }
        // ERC-1271 fallback.
        (bool ok, bytes memory ret) = expected.staticcall(abi.encodeWithSelector(EIP1271_MAGIC, digest, sig));
        return ok && ret.length == 32 && abi.decode(ret, (bytes4)) == EIP1271_MAGIC;
    }
}
