// File: src/BPSFeed.sol



// SPDX-License-Identifier: BUSL-1.1
/*
██████╗░██╗░░░░░░█████╗░░█████╗░███╗░░░███╗
██╔══██╗██║░░░░░██╔══██╗██╔══██╗████╗░████║
██████╦╝██║░░░░░██║░░██║██║░░██║██╔████╔██║
██╔══██╗██║░░░░░██║░░██║██║░░██║██║╚██╔╝██║
██████╦╝███████╗╚█████╔╝╚█████╔╝██║░╚═╝░██║
╚═════╝░╚══════╝░╚════╝░░╚════╝░╚═╝░░░░░╚═╝
*/

pragma solidity 0.8.19;

import {Owned} from "solmate/auth/Owned.sol";
import {IBPSFeed} from "./interfaces/IBPSFeed.sol";

contract BPSFeed is IBPSFeed, Owned {
    // =================== Storage ===================

    uint256 public currentRate;
    uint256 public lastTimestamp;
    uint256 internal _totalRate;
    uint256 internal _totalDuration;

    // ================== Constants ==================
    uint256 internal constant INITIAL_RATE = 1e4;
    uint256 internal constant MAX_RATE = 1.5e4;

    constructor() Owned(msg.sender) {
        currentRate = INITIAL_RATE;
    }

    /// @inheritdoc IBPSFeed
    function getWeightedRate() external view returns (uint256) {
        if (lastTimestamp == 0) return 0;

        uint256 lastRateDuration = block.timestamp - lastTimestamp;
        uint256 totalRate = _totalRate + currentRate * lastRateDuration;
        uint256 totalDuration = _totalDuration + lastRateDuration;
        return totalDuration == 0 ? 0 : totalRate / totalDuration;
    }

    /// @inheritdoc IBPSFeed
    function updateRate(uint256 _rate) external onlyOwner {
        if (_rate < INITIAL_RATE || _rate > MAX_RATE) {
            revert InvalidRate();
        }
        if (lastTimestamp > 0) {
            uint256 lastRateDuration = block.timestamp - lastTimestamp;
            _totalRate += currentRate * lastRateDuration;
            _totalDuration += lastRateDuration;
        }

        currentRate = _rate;
        lastTimestamp = block.timestamp;

        emit UpdateRate(currentRate);
    }
}

// File: lib/solmate/src/auth/Owned.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

// File: src/interfaces/IBPSFeed.sol



// SPDX-License-Identifier: BUSL-1.1
/*
██████╗░██╗░░░░░░█████╗░░█████╗░███╗░░░███╗
██╔══██╗██║░░░░░██╔══██╗██╔══██╗████╗░████║
██████╦╝██║░░░░░██║░░██║██║░░██║██╔████╔██║
██╔══██╗██║░░░░░██║░░██║██║░░██║██║╚██╔╝██║
██████╦╝███████╗╚█████╔╝╚█████╔╝██║░╚═╝░██║
╚═════╝░╚══════╝░╚════╝░░╚════╝░╚═╝░░░░░╚═╝
*/

pragma solidity 0.8.19;

interface IBPSFeed {
    error InvalidRate();

    event UpdateRate(uint256 currentRate);

    /// @notice Returns weighted rate
    function getWeightedRate() external view returns (uint256);

    /// @notice Returns current rate
    function currentRate() external view returns (uint256);

    /// @notice Returns last timestamp the rate was set
    function lastTimestamp() external view returns (uint256);

    /// @notice Sets new rate
    /// @param rate New rate
    function updateRate(uint256 rate) external;
}