// File: src/FeedRegistry.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {Auth} from "chronicle-std/auth/Auth.sol";

import {IFeedRegistry} from "./IFeedRegistry.sol";

/**
 * @title FeedRegistry
 *
 * @notice Single source of truth for all of Chronicle Protocol's feeds
 *
 * @dev This contract provides a registry for feeds via 1-byte feed ids.
 *
 *      A feeds feed id is computed as the highest-order byte of the feeds
 *      address, ie `uint8 feedId = uint8(uint(uint160(feed)) >> 152);`
 *
 *      Due to feed ids being 1 byte, the maximum number of feeds supported is
 *      256.
 *
 *      Note that a set of lifted feeds can be encoded in a single uint.
 *      The code refers to it as `uint bloom`.
 */
contract FeedRegistry is IFeedRegistry, Auth {
    /// @dev Statically allocated array for feeds.
    ///      Indexed via feed's feed id.
    address[256] internal _feeds;

    constructor(address initialAuthed) Auth(initialAuthed) {}

    /// @inheritdoc IFeedRegistry
    function feeds() external view returns (address[] memory) {
        address[] memory feeds_ = new address[](256);
        uint ctr;
        for (uint i; i < 256; i++) {
            address feed = _feeds[i];

            if (feed == address(0)) {
                continue;
            }

            feeds_[ctr++] = feed;
        }

        assembly ("memory-safe") {
            mstore(feeds_, ctr)
        }

        return feeds_;
    }

    /// @inheritdoc IFeedRegistry
    function feeds(address feed) public view returns (bool) {
        uint8 feedId = uint8(uint(uint160(feed)) >> 152);

        return _feeds[feedId] != address(0);
    }

    /// @inheritdoc IFeedRegistry
    function encode(address[] calldata feeds_) external view returns (uint) {
        uint bloom;

        for (uint i; i < feeds_.length; i++) {
            address feed = feeds_[i];

            if (!feeds(feed)) {
                revert FeedNotLifted(feed);
            }

            uint8 feedId = uint8(uint(uint160(feed)) >> 152);
            bloom |= (1 << feedId);
        }

        return bloom;
    }

    /// @inheritdoc IFeedRegistry
    function decode(uint bloom) external view returns (address[] memory) {
        address[] memory feeds_ = new address[](256);
        uint ctr;
        for (uint i; i < 256; i++) {
            if (bloom & (1 << i) == 0) {
                continue;
            }

            address feed = _feeds[i];

            if (feed == address(0)) {
                revert FeedIdNotLifted(uint8(i));
            }

            feeds_[ctr++] = feed;
        }

        assembly ("memory-safe") {
            mstore(feeds_, ctr)
        }

        return feeds_;
    }

    // -- Auth'ed Functionality --

    /// @inheritdoc IFeedRegistry
    function lift(address feed) external auth {
        _lift(feed);
    }

    /// @inheritdoc IFeedRegistry
    function lift(address[] memory feeds_) external auth {
        for (uint i; i < feeds_.length; i++) {
            _lift(feeds_[i]);
        }
    }

    /// @inheritdoc IFeedRegistry
    function drop(uint8 feedId) external auth {
        _drop(feedId);
    }

    /// @inheritdoc IFeedRegistry
    function drop(uint8[] memory feedIds) external auth {
        for (uint i; i < feedIds.length; i++) {
            _drop(feedIds[i]);
        }
    }

    // -- Internal Helpers --

    function _lift(address feed) internal {
        require(feed != address(0));

        uint8 feedId = uint8(uint(uint160(feed)) >> 152);

        address current = _feeds[feedId];
        if (current != feed) {
            require(current == address(0));

            _feeds[feedId] = feed;
            emit FeedLifted(msg.sender, feed);
        }
    }

    function _drop(uint8 feedId) internal {
        address current = _feeds[feedId];
        if (current != address(0)) {
            delete _feeds[feedId];
            emit FeedDropped(msg.sender, current);
        }
    }
}

/**
 * @dev Contract overwrite to deploy contract instances with specific naming.
 *
 *      For more info, see docs/Deployment.md.
 */
contract FeedRegistry_1 is FeedRegistry {
    constructor(address initialAuthed) FeedRegistry(initialAuthed) {}
}

// File: lib/chronicle-std/src/auth/Auth.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {IAuth} from "./IAuth.sol";

/**
 * @title Auth Module
 *
 * @dev The `Auth` contract module provides a basic access control mechanism,
 *      where a set of addresses are granted access to protected functions.
 *      These addresses are said to be _auth'ed_.
 *
 *      Initially, the address given as constructor argument is the only address
 *      auth'ed. Through the `rely(address)` and `deny(address)` functions,
 *      auth'ed callers are able to grant/renounce auth to/from addresses.
 *
 *      This module is used through inheritance. It will make available the
 *      modifier `auth`, which can be applied to functions to restrict their
 *      use to only auth'ed callers.
 */
abstract contract Auth is IAuth {
    /// @dev Mapping storing whether address is auth'ed.
    /// @custom:invariant Image of mapping is {0, 1}.
    ///                     ∀x ∊ Address: _wards[x] ∊ {0, 1}
    /// @custom:invariant Only address given as constructor argument is authenticated after deployment.
    ///                     deploy(initialAuthed) → (∀x ∊ Address: _wards[x] == 1 → x == initialAuthed)
    /// @custom:invariant Only functions `rely` and `deny` may mutate the mapping's state.
    ///                     ∀x ∊ Address: preTx(_wards[x]) != postTx(_wards[x])
    ///                                     → (msg.sig == "rely" ∨ msg.sig == "deny")
    /// @custom:invariant Mapping's state may only be mutated by authenticated caller.
    ///                     ∀x ∊ Address: preTx(_wards[x]) != postTx(_wards[x]) → _wards[msg.sender] = 1
    mapping(address => uint) private _wards;

    /// @dev List of addresses possibly being auth'ed.
    /// @dev May contain duplicates.
    /// @dev May contain addresses not being auth'ed anymore.
    /// @custom:invariant Every address being auth'ed once is element of the list.
    ///                     ∀x ∊ Address: authed(x) -> x ∊ _wardsTouched
    address[] private _wardsTouched;

    /// @dev Ensures caller is auth'ed.
    modifier auth() {
        assembly ("memory-safe") {
            // Compute slot of _wards[msg.sender].
            mstore(0x00, caller())
            mstore(0x20, _wards.slot)
            let slot := keccak256(0x00, 0x40)

            // Revert if caller not auth'ed.
            let isAuthed := sload(slot)
            if iszero(isAuthed) {
                // Store selector of `NotAuthorized(address)`.
                mstore(0x00, 0x4a0bfec1)
                // Store msg.sender.
                mstore(0x20, caller())
                // Revert with (offset, size).
                revert(0x1c, 0x24)
            }
        }
        _;
    }

    constructor(address initialAuthed) {
        _wards[initialAuthed] = 1;
        _wardsTouched.push(initialAuthed);

        // Note to use address(0) as caller to indicate address was auth'ed
        // during deployment.
        emit AuthGranted(address(0), initialAuthed);
    }

    /// @inheritdoc IAuth
    function rely(address who) external auth {
        if (_wards[who] == 1) return;

        _wards[who] = 1;
        _wardsTouched.push(who);
        emit AuthGranted(msg.sender, who);
    }

    /// @inheritdoc IAuth
    function deny(address who) external auth {
        if (_wards[who] == 0) return;

        _wards[who] = 0;
        emit AuthRenounced(msg.sender, who);
    }

    /// @inheritdoc IAuth
    function authed(address who) public view returns (bool) {
        return _wards[who] == 1;
    }

    /// @inheritdoc IAuth
    /// @custom:invariant Only contains auth'ed addresses.
    ///                     ∀x ∊ authed(): _wards[x] == 1
    /// @custom:invariant Contains all auth'ed addresses.
    ///                     ∀x ∊ Address: _wards[x] == 1 → x ∊ authed()
    function authed() public view returns (address[] memory) {
        // Initiate array with upper limit length.
        address[] memory wardsList = new address[](_wardsTouched.length);

        // Iterate through all possible auth'ed addresses.
        uint ctr;
        for (uint i; i < wardsList.length; i++) {
            // Add address only if still auth'ed.
            if (_wards[_wardsTouched[i]] == 1) {
                wardsList[ctr++] = _wardsTouched[i];
            }
        }

        // Set length of array to number of auth'ed addresses actually included.
        assembly ("memory-safe") {
            mstore(wardsList, ctr)
        }

        return wardsList;
    }

    /// @inheritdoc IAuth
    function wards(address who) public view returns (uint) {
        return _wards[who];
    }
}

// File: src/IFeedRegistry.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

interface IFeedRegistry {
    /// @notice Thrown if feed `feed` not lifted.
    error FeedNotLifted(address feed);

    /// @notice Thrown if feed id `feedId` not lifted.
    error FeedIdNotLifted(uint8 feedId);

    /// @notice Emitted when new feed lifted.
    /// @param caller The caller's address.
    /// @param feed The feed address lifted.
    event FeedLifted(address indexed caller, address indexed feed);

    /// @notice Emitted when feed dropped.
    /// @param caller The caller's address.
    /// @param feed The feed address dropped.
    event FeedDropped(address indexed caller, address indexed feed);

    // -- Public Read Functions --

    /// @notice Returns all of Chronicle Protocol's feeds.
    /// @return Chronicle Protocol's feeds.
    function feeds() external view returns (address[] memory);

    /// @notice Returns whether address `feed` is a feed.
    /// @param feed Feed address.
    /// @return True if address `feed` is feed, false otherwise.
    function feeds(address feed) external view returns (bool);

    /// @notice Returns list of feeds `feeds` in bloom encoding.
    ///
    /// @dev Reverts if:
    ///      - Any feed in `feeds` not a feed
    ///
    /// @param feeds The list of feeds to encode via bloom mechanism.
    /// @return Feeds encoded via bloom mechanism.
    function encode(address[] calldata feeds) external view returns (uint);

    /// @notice Returns list of feeds encoded in `bloom`.
    ///
    /// @dev Reverts if:
    ///      - Any feed encoded in `bloom` not a feed
    ///
    /// @param bloom The list feeds encoded via bloom mechanism.
    /// @return List of feed encoded in `bloom`.
    function decode(uint bloom) external view returns (address[] memory);

    // -- Auth'ed Functionality --

    /// @notice Lifts feed `feed_`.
    /// @dev Only callable by auth'ed address.
    /// @param feed The feed to lift.
    function lift(address feed) external;

    /// @notice Lifts feeds `feeds_`.
    /// @dev Only callable by auth'ed address.
    /// @param feeds The feeds to lift.
    function lift(address[] memory feeds) external;

    /// @notice Drops feed with feed id `feedId`.
    /// @dev Only callable by auth'ed address.
    /// @param feedId The feed id to drop.
    function drop(uint8 feedId) external;

    /// @notice Drops feeds with feed ids `feedIds`.
    /// @dev Only callable by auth'ed address.
    /// @param feedIds The feed ids to drop.
    function drop(uint8[] memory feedIds) external;
}

// File: lib/chronicle-std/src/auth/IAuth.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

interface IAuth {
    /// @notice Thrown by protected function if caller not auth'ed.
    /// @param caller The caller's address.
    error NotAuthorized(address caller);

    /// @notice Emitted when auth granted to address.
    /// @param caller The caller's address.
    /// @param who The address auth got granted to.
    event AuthGranted(address indexed caller, address indexed who);

    /// @notice Emitted when auth renounced from address.
    /// @param caller The caller's address.
    /// @param who The address auth got renounced from.
    event AuthRenounced(address indexed caller, address indexed who);

    /// @notice Grants address `who` auth.
    /// @dev Only callable by auth'ed address.
    /// @param who The address to grant auth.
    function rely(address who) external;

    /// @notice Renounces address `who`'s auth.
    /// @dev Only callable by auth'ed address.
    /// @param who The address to renounce auth.
    function deny(address who) external;

    /// @notice Returns whether address `who` is auth'ed.
    /// @param who The address to check.
    /// @return True if `who` is auth'ed, false otherwise.
    function authed(address who) external view returns (bool);

    /// @notice Returns full list of addresses granted auth.
    /// @dev May contain duplicates.
    /// @return List of addresses granted auth.
    function authed() external view returns (address[] memory);

    /// @notice Returns whether address `who` is auth'ed.
    /// @custom:deprecated Use `authed(address)(bool)` instead.
    /// @param who The address to check.
    /// @return 1 if `who` is auth'ed, 0 otherwise.
    function wards(address who) external view returns (uint);
}