// File: contracts/RouteConfigs.sol



// SPDX-License-Identifier: MIT

/* 

  _                          _   _____   _                       
 | |       ___   _ __     __| | |  ___| | |   __ _   _ __    ___ 
 | |      / _ \ | '_ \   / _` | | |_    | |  / _` | | '__|  / _ \
 | |___  |  __/ | | | | | (_| | |  _|   | | | (_| | | |    |  __/
 |_____|  \___| |_| |_|  \__,_| |_|     |_|  \__,_| |_|     \___|
                                                                 
LendFlare.finance
*/

pragma solidity ^0.8.0;

import "./base/Multicall.sol";

contract RouteConfigs is Multicall {
    address public owner;
    address public governance;

    struct Dynamic {
        bool isExist;
        uint256 value;
    }

    mapping(string => uint256[]) public rules;
    mapping(string => uint256) public configs;
    mapping(address => mapping(string => Dynamic)) public dynamics;

    event SetOwner(address owner);
    event SetGovernance(address governance);
    event UpdateConfig(string key, uint256 oldValue, uint256 newValue);

    modifier onlyOwner() {
        require(owner == msg.sender, "RouteConfigs: caller is not the owner");
        _;
    }

    modifier onlyGovernance() {
        require(governance == msg.sender, "RouteConfigs: caller is not the governance");
        _;
    }

    constructor(address _owner) {
        owner = _owner;
        governance = _owner;

        rules["MAX_OVERFLOW_BALANCE"] = [100, 200];
        rules["MAX_OVERFLOW_RATIO"] = [10, 100];

        _updateConfig("MAX_OVERFLOW_BALANCE", 150);
        _updateConfig("MAX_OVERFLOW_RATIO", 85);
    }

    function setOwner(address _owner) public onlyOwner {
        owner = _owner;

        emit SetOwner(_owner);
    }

    function setGovernance(address _governance) public onlyOwner {
        governance = _governance;

        emit SetGovernance(_governance);
    }

    function updateRule(string memory _key, uint256[2] calldata _rules) public onlyGovernance {
        require(bytes(_key).length > 0, "!_key");
        require(_rules[0] != _rules[1], "!_rules");

        if (_rules[0] == 0 && _rules[1] == 0) {
            delete rules[_key];

            return;
        }

        rules[_key] = _rules;
    }

    function _updateConfig(string memory _key, uint256 _value) internal {
        emit UpdateConfig(_key, configs[_key], _value);

        delete configs[_key];

        configs[_key] = _value;
    }

    function updateConfig(string memory _key, uint256 _value) public onlyGovernance {
        require(bytes(_key).length > 0, "!_key");
        require(rules[_key].length > 0, "!Need to add rules first");

        if (rules[_key].length > 0) {
            require((_value >= rules[_key][0]) && (_value <= rules[_key][1]), "!value");
        }

        _updateConfig(_key, _value);
    }

    function updateDynamicConfig(
        address _target,
        string memory _key,
        uint256 _value
    ) public onlyGovernance {
        require(_target != address(0), "!_target");
        require(bytes(_key).length > 0, "!_key");
        require(rules[_key].length > 0, "!Need to add rules first");

        if (rules[_key].length > 0) {
            require((_value >= rules[_key][0]) && (_value <= rules[_key][1]), "!value");
        }

        dynamics[_target][_key].isExist = true;
        dynamics[_target][_key].value = _value;
    }

    function removeConfig(string memory _key) public onlyGovernance {
        require(bytes(_key).length > 0, "!_key");

        delete configs[_key];
        delete rules[_key];
    }

    function removeDynamicConfig(address _target, string memory _key) public onlyGovernance {
        require(_target != address(0), "!_target");
        require(bytes(_key).length > 0, "!_key");

        delete dynamics[_target][_key];
    }

    function getConfig(string memory _key) external view returns (uint256) {
        if (dynamics[msg.sender][_key].isExist) return dynamics[msg.sender][_key].value;

        return configs[_key];
    }
}

// File: contracts/base/Multicall.sol



// SPDX-License-Identifier: MIT

/* 

  _                          _   _____   _                       
 | |       ___   _ __     __| | |  ___| | |   __ _   _ __    ___ 
 | |      / _ \ | '_ \   / _` | | |_    | |  / _` | | '__|  / _ \
 | |___  |  __/ | | | | | (_| | |  _|   | | | (_| | | |    |  __/
 |_____|  \___| |_| |_|  \__,_| |_|     |_|  \__,_| |_|     \___|
                                                                 
LendFlare.finance
*/

pragma solidity ^0.8.0;
pragma abicoder v2;

import "../interfaces/IMulticall.sol";

/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
    /// @inheritdoc IMulticall
    function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                // Next 5 lines from https://ethereum.stackexchange.com/a/83577
                if (result.length < 68) revert();
                assembly {
                    result := add(result, 0x04)
                }
                revert(abi.decode(result, (string)));
            }

            results[i] = result;
        }
    }
}

// File: contracts/interfaces/IMulticall.sol



// SPDX-License-Identifier: MIT

/* 

  _                          _   _____   _                       
 | |       ___   _ __     __| | |  ___| | |   __ _   _ __    ___ 
 | |      / _ \ | '_ \   / _` | | |_    | |  / _` | | '__|  / _ \
 | |___  |  __/ | | | | | (_| | |  _|   | | | (_| | | |    |  __/
 |_____|  \___| |_| |_|  \__,_| |_|     |_|  \__,_| |_|     \___|
                                                                 
LendFlare.finance
*/

pragma solidity ^0.8.0;
pragma abicoder v2;

/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
    /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
    /// @dev The `msg.value` should not be trusted for any method callable from multicall.
    /// @param data The encoded function data for each of the calls to make to this contract
    /// @return results The results from each of the calls passed in via data
    function multicall(bytes[] calldata data)
        external
        payable
        returns (bytes[] memory results);
}