// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

/// @title Multicall.
/// @notice Contract that batches view function calls and aggregates their results.
/// @author Adapted from Makerdao's Multicall2 (https://github.com/makerdao/multicall/blob/master/src/Multicall2.sol).

contract Multicall {
    /// @dev 0x
    error CallFailed(string reason);

    struct Call {
        address target;
        bytes callData;
    }
    struct Result {
        bool success;
        bytes returnData;
    }

    //prettier-ignore
    constructor(/*  */) payable {/*  */}

    function mtc1(Call[] calldata calls)
        external
        returns (uint256, bytes[] memory)
    {
        uint256 bn = block.number;
        uint256 len = calls.length;
        bytes[] memory res = new bytes[](len);
        uint256 j;

        while (j < len) {
            (bool success, bytes memory ret) = calls[j]
                .target
                .call(calls[j].callData);
            if (!success) {
                if (ret.length < 0x44) revert CallFailed("");
                assembly {
                    ret := add(ret, 0x04)
                }
                revert CallFailed({
                    reason: abi.decode(ret, (string))
                });
            }
            res[j] = ret;
            ++j;
        }
        return (bn, res);
    }

    function mtc2(Call[] calldata calls)
        external
        returns (
            uint256,
            bytes32,
            Result[] memory
        )
    {
        uint256 bn = block.number;
        // µ 0 s [0] ≡ P(IHp , µs [0], 0) ∴ P is the hash of a block of a particular number, up to a maximum age.
        // 0 is left on the stack if the looked for `block.number` is >= to the current `block.number` or more than 256
        // blocks behind the current block (Yellow Paper, p. 33, https://ethereum.github.io/yellowpaper/paper.pdf).
        bytes32 bh = blockhash(
            bn /* - 1 */
        );
        uint256 len = calls.length;
        Result[] memory res = new Result[](len);
        uint256 i;
        for (i; i < len; ) {
            (bool success, bytes memory ret) = calls[i]
                .target
                .call(calls[i].callData);

            res[i] = Result(success, ret);
            ++i;
        }
        return (bn, bh, res);
    }
}
