contract MultiSender {

    // Internal Functions
    function _sendETH(address payable address_, uint256 amount_) internal {
        (bool success, ) = payable(address_).call{value: amount_}("");
        require(success, "Transfer failed");
    }
    function _calculateTotalAmounts(uint256[] memory amounts_) internal pure
    returns (uint256 _totalAmount) {
        for (uint256 i = 0; i < amounts_.length; i++) {
            _totalAmount += amounts_[i];
        }
    }

    function multiSendETH(address payable[] calldata addresses_, 
    uint256[] calldata amounts_, bool useChecks_) external payable {
        require(addresses_.length == amounts_.length,
            "Array lengths mismatch!");

        // We use loop checks but we can opt out to save gas
        if (useChecks_) {
            // Get the Total Amount
            uint256 _totalAmount = _calculateTotalAmounts(amounts_);

            require(msg.value == _totalAmount,
                "Invalid amount of ETH sent!");
        }

        // Multi-Send the ETHs
        for (uint256 i = 0; i < addresses_.length; i++) {
            _sendETH(addresses_[i], amounts_[i]);
        }
    }

    function multiSendERC20(address erc20_, address[] calldata addresses_,
    uint256[] calldata amounts_, bool useChecks_) external {
        require(addresses_.length == amounts_.length,
            "Array lengths mismatch!");

        // We use loop checks but we can opt out to save gas
        if (useChecks_) {
            // Get the Total Amount
            uint256 _totalAmount = _calculateTotalAmounts(amounts_);

            // Make sure the msg.sender has enough ETH
            require(IERC20(erc20_).balanceOf(msg.sender) >= _totalAmount,
                "msg.sender does not have enough balance!");
        }

        // Multi-Send ERC20s
        for (uint256 i = 0; i < addresses_.length; i++) {
            IERC20(erc20_).transferFrom(msg.sender, addresses_[i], amounts_[i]);
        }
    }

    function multiSendERC721(address erc721_, address[] calldata addresses_,
    uint256[] calldata tokenIds_, bool useChecks_) external {
        require(addresses_.length == tokenIds_.length,
            "Array lengths mismatch!");

        if (useChecks_) {
            for (uint256 i = 0; i < tokenIds_.length; i++) {
                require(msg.sender == IERC721(erc721_).ownerOf(tokenIds_[i]),
                    "You are not the owner of this token!");
            }
        }

        // Multi-Send ERC721s
        for (uint256 i = 0; i < addresses_.length; i++) {
            IERC721(erc721_).transferFrom(msg.sender, addresses_[i], tokenIds_[i]);
        }
    }

    function multiSendERC1155(address erc1155_, address[] calldata addresses_,
    uint256[] calldata tokenIds_, uint256[] calldata amounts_,
    bytes calldata data_) external {
        require(addresses_.length == tokenIds_.length 
            && addresses_.length == amounts_.length,
            "Array lengths mismatch!");

        // No checks for this one.
        for (uint256 i = 0; i < addresses_.length; i++) {
            IERC1155(erc1155_).safeTransferFrom(msg.sender, addresses_[i], tokenIds_[i],
            amounts_[i], data_);
        }
    }

}
function _sendETH(address payable address_, uint256 amount_) internal {
    (bool success, ) = payable(address_).call{value: amount_}("");
    require(success, "Transfer failed");
}

// Internal Functions