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

import "./IDBIS_EIP712Helper.sol";

/**
 * @title DBIS_EIP712Helper
 * @notice Helper contract for EIP-712 hashing and ecrecover (own stack when called).
 */
contract DBIS_EIP712Helper is IDBIS_EIP712Helper {
    function hashAddressArray(address[] calldata arr) external pure override returns (bytes32) {
        bytes32[] memory hashes = new bytes32[](arr.length);
        for (uint256 i = 0; i < arr.length; i++) {
            hashes[i] = keccak256(abi.encode(arr[i]));
        }
        return keccak256(abi.encodePacked(hashes));
    }

    function hashUint256Array(uint256[] calldata arr) external pure override returns (bytes32) {
        bytes32[] memory hashes = new bytes32[](arr.length);
        for (uint256 i = 0; i < arr.length; i++) {
            hashes[i] = keccak256(abi.encode(arr[i]));
        }
        return keccak256(abi.encodePacked(hashes));
    }

    function getMintAuthStructHash(
        bytes32 typeHash,
        bytes32 messageId,
        bytes32 isoType,
        bytes32 isoHash,
        bytes32 accountingRef,
        uint8 fundsStatus,
        bytes32 corridor,
        uint8 assetClass,
        bytes32 recipientsHash,
        bytes32 amountsHash,
        uint64 notBefore,
        uint64 expiresAt,
        uint256 chainId,
        address verifyingContract
    ) external pure override returns (bytes32) {
        return keccak256(abi.encode(
            typeHash,
            messageId,
            isoType,
            isoHash,
            accountingRef,
            fundsStatus,
            corridor,
            assetClass,
            recipientsHash,
            amountsHash,
            notBefore,
            expiresAt,
            chainId,
            verifyingContract
        ));
    }

    function getDigest(bytes32 domainSeparator, bytes32 structHash) external pure override returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }

    function recover(bytes32 digest, bytes calldata signature) external pure override returns (address) {
        return _recover(digest, signature);
    }

    function recoverSigners(bytes32 digest, bytes[] calldata signatures) external pure override returns (address[] memory signers) {
        uint256 n = signatures.length;
        signers = new address[](n);
        unchecked {
            for (uint256 i; i < n; i++) {
                signers[i] = _recover(digest, signatures[i]);
            }
        }
    }

    function getSwapAuthDigest(
        bytes32 domainSeparator,
        bytes32 typeHash,
        bytes32 messageId,
        bytes32 lpaId,
        bytes32 venue,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        uint256 deadline,
        bytes32 quoteHash,
        address quoteIssuer,
        uint256 chainId,
        address verifyingContract
    ) external pure override returns (bytes32) {
        bytes32 structHash = keccak256(abi.encode(
            typeHash,
            messageId,
            lpaId,
            venue,
            tokenIn,
            tokenOut,
            amountIn,
            minAmountOut,
            deadline,
            quoteHash,
            quoteIssuer,
            chainId,
            verifyingContract
        ));
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }

    function _recover(bytes32 digest, bytes calldata signature) private pure returns (address) {
        require(signature.length == 65, "DBIS: sig length");
        bytes32 r;
        bytes32 s;
        uint8 v;
        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }
        if (v < 27) v += 27;
        require(v == 27 || v == 28, "DBIS: invalid v");
        return ecrecover(digest, v, r, s);
    }
}
