// SPDX-License-Identifier: MIT

// MIT License

// Copyright (c) 2022 Optimism
// Copyright (c) 2022 Scroll

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

pragma solidity =0.8.24;

import {IL2ScrollMessenger} from "../IL2ScrollMessenger.sol";
import {OwnableBase} from "../../libraries/common/OwnableBase.sol";

// solhint-disable no-empty-blocks
// solhint-disable reason-string

/// @title L2TxFeeVault
/// @notice The L2TxFeeVault contract contains the logic for the vault contracts
///         used to hold fee revenue generated by the L2 system.
contract L2TxFeeVault is OwnableBase {
    /**********
     * Events *
     **********/

    /// @notice Emits each time that a withdrawal occurs.
    ///
    /// @param value Amount that was withdrawn (in wei).
    /// @param to    Address that the funds were sent to.
    /// @param from  Address that triggered the withdrawal.
    event Withdrawal(uint256 value, address to, address from);

    /// @notice Emits each time the owner updates the address of `messenger`.
    /// @param oldMessenger The address of old messenger.
    /// @param newMessenger The address of new messenger.
    event UpdateMessenger(address indexed oldMessenger, address indexed newMessenger);

    /// @notice Emits each time the owner updates the address of `recipient`.
    /// @param oldRecipient The address of old recipient.
    /// @param newRecipient The address of new recipient.
    event UpdateRecipient(address indexed oldRecipient, address indexed newRecipient);

    /// @notice Emits each time the owner updates the value of `minWithdrawAmount`.
    /// @param oldMinWithdrawAmount The value of old `minWithdrawAmount`.
    /// @param newMinWithdrawAmount The value of new `minWithdrawAmount`.
    event UpdateMinWithdrawAmount(uint256 oldMinWithdrawAmount, uint256 newMinWithdrawAmount);

    /*************
     * Variables *
     *************/

    /// @notice Minimum balance before a withdrawal can be triggered.
    uint256 public minWithdrawAmount;

    /// @notice Scroll L2 messenger address.
    address public messenger;

    /// @notice Wallet that will receive the fees on L1.
    address public recipient;

    /// @notice Total amount of wei processed by the contract.
    uint256 public totalProcessed;

    /***************
     * Constructor *
     ***************/

    /// @param _owner               The owner of the contract.
    /// @param _recipient           Wallet that will receive the fees on L1.
    /// @param _minWithdrawalAmount Minimum balance before a withdrawal can be triggered.
    constructor(
        address _owner,
        address _recipient,
        uint256 _minWithdrawalAmount
    ) {
        _transferOwnership(_owner);

        minWithdrawAmount = _minWithdrawalAmount;
        recipient = _recipient;
    }

    /*****************************
     * Public Mutating Functions *
     *****************************/

    /// @notice Allow the contract to receive ETH.
    receive() external payable {}

    /// @notice Triggers a withdrawal of funds to the L1 fee wallet.
    /// @param _value The amount of ETH to withdraw.
    function withdraw(uint256 _value) public {
        require(
            _value >= minWithdrawAmount,
            "FeeVault: withdrawal amount must be greater than minimum withdrawal amount"
        );

        uint256 _balance = address(this).balance;
        require(_value <= _balance, "FeeVault: insufficient balance to withdraw");

        unchecked {
            totalProcessed += _value;
        }

        emit Withdrawal(_value, recipient, msg.sender);

        // no fee provided
        IL2ScrollMessenger(messenger).sendMessage{value: _value}(
            recipient,
            _value,
            bytes(""), // no message (simple eth transfer)
            0 // _gasLimit can be zero for fee vault.
        );
    }

    /// @notice Triggers a withdrawal of all available funds to the L1 fee wallet.
    function withdraw() external {
        uint256 value = address(this).balance;
        withdraw(value);
    }

    /************************
     * Restricted Functions *
     ************************/

    /// @notice Update the address of messenger.
    /// @param _newMessenger The address of messenger to update.
    function updateMessenger(address _newMessenger) external onlyOwner {
        address _oldMessenger = messenger;
        messenger = _newMessenger;

        emit UpdateMessenger(_oldMessenger, _newMessenger);
    }

    /// @notice Update the address of recipient.
    /// @param _newRecipient The address of recipient to update.
    function updateRecipient(address _newRecipient) external onlyOwner {
        address _oldRecipient = recipient;
        recipient = _newRecipient;

        emit UpdateRecipient(_oldRecipient, _newRecipient);
    }

    /// @notice Update the minimum withdraw amount.
    /// @param _newMinWithdrawAmount The minimum withdraw amount to update.
    function updateMinWithdrawAmount(uint256 _newMinWithdrawAmount) external onlyOwner {
        uint256 _oldMinWithdrawAmount = minWithdrawAmount;
        minWithdrawAmount = _newMinWithdrawAmount;

        emit UpdateMinWithdrawAmount(_oldMinWithdrawAmount, _newMinWithdrawAmount);
    }
}
