// File: src/WatRegistry.sol



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

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

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

/**
 * @title WatRegistry
 *
 * @notice Registry to manage feed and bar configurations for non-mainnet wats
 *
 * @dev The contract uses the `chronicle-std/Auth` module for access control.
 *      While the registry is publicly readable, state mutating functions are
 *      only callable by auth'ed addresses.
 */
contract WatRegistry is IWatRegistry, Auth {
    /// @dev Mapping storing a wat's bar configuration.
    mapping(bytes32 => uint8) internal _bar;

    /// @dev Mapping storing a wat's list of lifted feeds.
    ///
    /// @custom:invariant Every feed for every wat maps to 1 in _feedsLookup[wat].
    ///                     ∀wat ∊ Bytes32: ∀x ∊ Address: x ∊ _feeds[wat] → _feedsLookup[wat][x] = 1
    /// @custom:invariant List does not contain duplicates.
    ///                     ∀wat ∊ Bytes32: ∀x ∊ Address: count(_feeds[wat], x) <= 1
    mapping(bytes32 => address[]) internal _feeds;

    /// @dev Mapping storing whether a wat has a feed lifted.
    /// @dev Used to prevent duplicates in _feeds[wat].
    ///
    /// @custom:invariant Image of mapping is {0, 1}
    ///                     ∀wat ∊ Bytes32: ∀x ∊ Address: _feedsLookup[wat][x] ∊ {0, 1}
    mapping(bytes32 => mapping(address => uint)) _feedsLookup;

    // -- Constructor --

    constructor(address initialAuthed) Auth(initialAuthed) {}

    // -- Public View Functions --

    /// @inheritdoc IWatRegistry
    function bar(bytes32 wat) external view returns (uint8) {
        return _bar[wat];
    }

    /// @inheritdoc IWatRegistry
    function feeds(bytes32 wat) external view returns (address[] memory) {
        return _feeds[wat];
    }

    // -- Auth'ed Functions --

    /// @inheritdoc IWatRegistry
    function setBar(bytes32 wat, uint8 bar_) external auth {
        if (_bar[wat] != bar_) {
            emit BarUpdated(msg.sender, wat, _bar[wat], bar_);

            _bar[wat] = bar_;
        }
    }

    /// @inheritdoc IWatRegistry
    function lift(bytes32 wat, address feed) external auth {
        _lift(wat, feed);
    }

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

    /// @inheritdoc IWatRegistry
    function drop(bytes32 wat, address feed) external auth {
        _drop(wat, feed);
    }

    /// @inheritdoc IWatRegistry
    function drop(bytes32 wat, address[] memory feeds_) external auth {
        for (uint i; i < feeds_.length; i++) {
            _drop(wat, feeds_[i]);
        }
    }

    // -- Internal Helpers --

    function _lift(bytes32 wat, address feed) internal {
        if (_feedsLookup[wat][feed] == 1) return;

        _feedsLookup[wat][feed] = 1;
        _feeds[wat].push(feed);

        emit FeedLifted(msg.sender, wat, feed);
    }

    function _drop(bytes32 wat, address feed) internal {
        if (_feedsLookup[wat][feed] == 0) return;

        _feedsLookup[wat][feed] = 0;

        address[] storage feeds_ = _feeds[wat];
        for (uint i; i < feeds_.length; i++) {
            if (feeds_[i] == feed) {
                feeds_[i] = feeds_[feeds_.length - 1];
                feeds_.pop();

                emit FeedDropped(msg.sender, wat, feed);

                break;
            }
        }
    }
}

/**
 * @dev Contract overwrite to deploy contract instances with specific naming.
 *
 *      For more info, see docs/Deployment.md.
 */
contract WatRegistry_1 is WatRegistry {
    constructor(address initialAuthed) WatRegistry(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/IWatRegistry.sol



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

interface IWatRegistry {
    /// @notice Emitted when a wat's bar updated.
    /// @param caller The caller's address.
    /// @param wat The wat bar was updated for.
    /// @param oldBar The old bar's value.
    /// @param newBar The new bar's value.
    event BarUpdated(
        address indexed caller, bytes32 indexed wat, uint8 oldBar, uint8 newBar
    );

    /// @notice Emitted when new feed lifted for a wat.
    /// @param caller The caller's address.
    /// @param wat The wat the feed was lifted for.
    /// @param feed The feed address lifted.
    event FeedLifted(
        address indexed caller, bytes32 indexed wat, address indexed feed
    );

    /// @notice Emitted when a wat's feed dropped.
    /// @param caller The caller's address.
    /// @param wat The wat the feed was dropped for.
    /// @param feed The feed address dropped.
    event FeedDropped(
        address indexed caller, bytes32 indexed wat, address indexed feed
    );

    // -- View Functionality --

    /// @notice Returns the bar configuration for wat `wat`.
    /// @param wat The wat of which to return its bar configuration.
    /// @return bar The bar configuration for wat `wat`.
    function bar(bytes32 wat) external view returns (uint8 bar);

    /// @notice Returns the list of feeds lifted for wat `wat`.
    /// @param wat The wat of which to return its lifted feeds.
    /// @return feeds The lifted feeds for wat `wat`.
    function feeds(bytes32 wat)
        external
        view
        returns (address[] memory feeds);

    // -- Auth'ed Functionality --

    /// @notice Updates the bar configuration for wat `wat` to `bar`.
    /// @dev Only callable by auth'ed address.
    /// @param wat The wat to update its bar.
    /// @param bar The bar configuration to set for wat `wat`.
    function setBar(bytes32 wat, uint8 bar) external;

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

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

    /// @notice Drops feed `feed` for wat `wat`.
    /// @dev Only callable by auth'ed address.
    /// @param wat The wat to drop feed `feed` for.
    /// @param feed The feed to drop for wat `wat`.
    function drop(bytes32 wat, address feed) external;

    /// @notice Drops feeds `feeds` for wat `wat`.
    /// @dev Only callable by auth'ed address.
    /// @param wat The wat to drop feeds `feeds` for.
    /// @param feeds The feeds to drop for wat `wat`.
    function drop(bytes32 wat, address[] memory feeds) 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);
}