creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n  function decimals() external view returns (uint8);\n\n  function description() external view returns (string memory);\n\n  function version() external view returns (uint256);\n\n  function getRoundData(uint80 _roundId)\n    external\n    view\n    returns (\n      uint80 roundId,\n      int256 answer,\n      uint256 startedAt,\n      uint256 updatedAt,\n      uint80 answeredInRound\n    );\n\n  function latestRoundData()\n    external\n    view\n    returns (\n      uint80 roundId,\n      int256 answer,\n      uint256 startedAt,\n      uint256 updatedAt,\n      uint80 answeredInRound\n    );\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControlUpgradeable {\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) external;\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20PermitUpgradeable {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "contracts/interfaces/IAsset.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"../libraries/Fixed.sol\";\nimport \"./IMain.sol\";\nimport \"./IRewardable.sol\";\n\n// Not used directly in the IAsset interface, but used by many consumers to save stack space\nstruct Price {\n    uint192 low; // {UoA/tok}\n    uint192 high; // {UoA/tok}\n}\n\n/**\n * @title IAsset\n * @notice Supertype. Any token that interacts with our system must be wrapped in an asset,\n * whether it is used as RToken backing or not. Any token that can report a price in the UoA\n * is eligible to be an asset.\n */\ninterface IAsset is IRewardable {\n    /// Refresh saved price\n    /// The Reserve protocol calls this at least once per transaction, before relying on\n    /// the Asset's other functions.\n    /// @dev Called immediately after deployment, before use\n    function refresh() external;\n\n    /// Should not revert\n    /// @return low {UoA/tok} The lower end of the price estimate\n    /// @return high {UoA/tok} The upper end of the price estimate\n    function price() external view returns (uint192 low, uint192 high);\n\n    /// Should not revert\n    /// lotLow should be nonzero when the asset might be worth selling\n    /// @return lotLow {UoA/tok} The lower end of the lot price estimate\n    /// @return lotHigh {UoA/tok} The upper end of the lot price estimate\n    function lotPrice() external view returns (uint192 lotLow, uint192 lotHigh);\n\n    /// @return {tok} The balance of the ERC20 in whole tokens\n    function bal(address account) external view returns (uint192);\n\n    /// @return The ERC20 contract of the token with decimals() available\n    function erc20() external view returns (IERC20Metadata);\n\n    /// @return The number of decimals in the ERC20; just for gas optimization\n    function erc20Decimals() external view returns (uint8);\n\n    /// @return If the asset is an instance of ICollateral or not\n    function isCollateral() external view returns (bool);\n\n    /// @return {UoA} The max trade volume, in UoA\n    function maxTradeVolume() external view returns (uint192);\n\n    /// @return {s} The timestamp of the last refresh() that saved prices\n    function lastSave() external view returns (uint48);\n}\n\n// Used only in Testing. Strictly speaking an Asset does not need to adhere to this interface\ninterface TestIAsset is IAsset {\n    /// @return The address of the chainlink feed\n    function chainlinkFeed() external view returns (AggregatorV3Interface);\n\n    /// {1} The max % deviation allowed by the oracle\n    function oracleError() external view returns (uint192);\n\n    /// @return {s} Seconds that an oracle value is considered valid\n    function oracleTimeout() external view returns (uint48);\n\n    /// @return {s} Seconds that the lotPrice should decay over, after stale price\n    function priceTimeout() external view returns (uint48);\n}\n\n/// CollateralStatus must obey a linear ordering. That is:\n/// - being DISABLED is worse than being IFFY, or SOUND\n/// - being IFFY is worse than being SOUND.\nenum CollateralStatus {\n    SOUND,\n    IFFY, // When a peg is not holding or a chainlink feed is stale\n    DISABLED // When the collateral has completely defaulted\n}\n\n/// Upgrade-safe maximum operator for CollateralStatus\nlibrary CollateralStatusComparator {\n    /// @return Whether a is worse than b\n    function worseThan(CollateralStatus a, CollateralStatus b) internal pure returns (bool) {\n        return uint256(a) > uint256(b);\n    }\n}\n\n/**\n * @title ICollateral\n * @notice A subtype of Asset that consists of the tokens eligible to back the RToken.\n */\ninterface ICollateral is IAsset {\n    /// Emitted whenever the collateral status is changed\n    /// @param newStatus The old CollateralStatus\n    /// @param newStatus The updated CollateralStatus\n    event CollateralStatusChanged(\n        CollateralStatus indexed oldStatus,\n        CollateralStatus indexed newStatus\n    );\n\n    /// @dev refresh()\n    /// Refresh exchange rates and update default status.\n    /// VERY IMPORTANT: In any valid implemntation, status() MUST become DISABLED in refresh() if\n    /// refPerTok() has ever decreased since last call.\n\n    /// @return The canonical name of this collateral's target unit.\n    function targetName() external view returns (bytes32);\n\n    /// @return The status of this collateral asset. (Is it defaulting? Might it soon?)\n    function status() external view returns (CollateralStatus);\n\n    // ==== Exchange Rates ====\n\n    /// @return {ref/tok} Quantity of whole reference units per whole collateral tokens\n    function refPerTok() external view returns (uint192);\n\n    /// @return {target/ref} Quantity of whole target units per whole reference unit in the peg\n    function targetPerRef() external view returns (uint192);\n}\n\n// Used only in Testing. Strictly speaking a Collateral does not need to adhere to this interface\ninterface TestICollateral is TestIAsset, ICollateral {\n    /// @return The epoch timestamp when the collateral will default from IFFY to DISABLED\n    function whenDefault() external view returns (uint256);\n\n    /// @return The amount of time a collateral must be in IFFY status until being DISABLED\n    function delayUntilDefault() external view returns (uint48);\n}\n"
      },
      "contracts/interfaces/IAssetRegistry.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"./IAsset.sol\";\nimport \"./IComponent.sol\";\n\n/// A serialization of the AssetRegistry to be passed around in the P1 impl for gas optimization\nstruct Registry {\n    IERC20[] erc20s;\n    IAsset[] assets;\n}\n\n/**\n * @title IAssetRegistry\n * @notice The AssetRegistry is in charge of maintaining the ERC20 tokens eligible\n *   to be handled by the rest of the system. If an asset is in the registry, this means:\n *      1. Its ERC20 contract has been vetted\n *      2. The asset is the only asset for that ERC20\n *      3. The asset can be priced in the UoA, usually via an oracle\n */\ninterface IAssetRegistry is IComponent {\n    /// Emitted when an asset is added to the registry\n    /// @param erc20 The ERC20 contract for the asset\n    /// @param asset The asset contract added to the registry\n    event AssetRegistered(IERC20 indexed erc20, IAsset indexed asset);\n\n    /// Emitted when an asset is removed from the registry\n    /// @param erc20 The ERC20 contract for the asset\n    /// @param asset The asset contract removed from the registry\n    event AssetUnregistered(IERC20 indexed erc20, IAsset indexed asset);\n\n    // Initialization\n    function init(IMain main_, IAsset[] memory assets_) external;\n\n    /// Fully refresh all asset state\n    /// @custom:interaction\n    function refresh() external;\n\n    /// Register `asset`\n    /// If either the erc20 address or the asset was already registered, fail\n    /// @return true if the erc20 address was not already registered.\n    /// @custom:governance\n    function register(IAsset asset) external returns (bool);\n\n    /// Register `asset` if and only if its erc20 address is already registered.\n    /// If the erc20 address was not registered, revert.\n    /// @return swapped If the asset was swapped for a previously-registered asset\n    /// @custom:governance\n    function swapRegistered(IAsset asset) external returns (bool swapped);\n\n    /// Unregister an asset, requiring that it is already registered\n    /// @custom:governance\n    function unregister(IAsset asset) external;\n\n    /// @return {s} The timestamp of the last refresh\n    function lastRefresh() external view returns (uint48);\n\n    /// @return The corresponding asset for ERC20, or reverts if not registered\n    function toAsset(IERC20 erc20) external view returns (IAsset);\n\n    /// @return The corresponding collateral, or reverts if unregistered or not collateral\n    function toColl(IERC20 erc20) external view returns (ICollateral);\n\n    /// @return If the ERC20 is registered\n    function isRegistered(IERC20 erc20) external view returns (bool);\n\n    /// @return A list of all registered ERC20s\n    function erc20s() external view returns (IERC20[] memory);\n\n    /// @return reg The list of registered ERC20s and Assets, in the same order\n    function getRegistry() external view returns (Registry memory reg);\n\n    /// @return The number of registered ERC20s\n    function size() external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IBackingManager.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"./IBroker.sol\";\nimport \"./IComponent.sol\";\nimport \"./ITrading.sol\";\n\n/**\n * @title IBackingManager\n * @notice The BackingManager handles changes in the ERC20 balances that back an RToken.\n *   - It computes which trades to perform, if any, and initiates these trades with the Broker.\n *     - rebalance()\n *   - If already collateralized, excess assets are transferred to RevenueTraders.\n *     - forwardRevenue(IERC20[] calldata erc20s)\n */\ninterface IBackingManager is IComponent, ITrading {\n    /// Emitted when the trading delay is changed\n    /// @param oldVal The old trading delay\n    /// @param newVal The new trading delay\n    event TradingDelaySet(uint48 oldVal, uint48 newVal);\n\n    /// Emitted when the backing buffer is changed\n    /// @param oldVal The old backing buffer\n    /// @param newVal The new backing buffer\n    event BackingBufferSet(uint192 oldVal, uint192 newVal);\n\n    // Initialization\n    function init(\n        IMain main_,\n        uint48 tradingDelay_,\n        uint192 backingBuffer_,\n        uint192 maxTradeSlippage_,\n        uint192 minTradeVolume_\n    ) external;\n\n    // Give RToken max allowance over a registered token\n    /// @custom:refresher\n    /// @custom:interaction\n    function grantRTokenAllowance(IERC20) external;\n\n    /// Apply the overall backing policy using the specified TradeKind, taking a haircut if unable\n    /// @param kind TradeKind.DUTCH_AUCTION or TradeKind.BATCH_AUCTION\n    /// @custom:interaction RCEI\n    function rebalance(TradeKind kind) external;\n\n    /// Forward revenue to RevenueTraders; reverts if not fully collateralized\n    /// @param erc20s The tokens to forward\n    /// @custom:interaction RCEI\n    function forwardRevenue(IERC20[] calldata erc20s) external;\n}\n\ninterface TestIBackingManager is IBackingManager, TestITrading {\n    function tradingDelay() external view returns (uint48);\n\n    function backingBuffer() external view returns (uint192);\n\n    function setTradingDelay(uint48 val) external;\n\n    function setBackingBuffer(uint192 val) external;\n}\n"
      },
      "contracts/interfaces/IBasketHandler.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../libraries/Fixed.sol\";\nimport \"./IAsset.sol\";\nimport \"./IComponent.sol\";\n\nstruct BasketRange {\n    uint192 bottom; // {BU}\n    uint192 top; // {BU}\n}\n\n/**\n * @title IBasketHandler\n * @notice The BasketHandler aims to maintain a reference basket of constant target unit amounts.\n * When a collateral token defaults, a new reference basket of equal target units is set.\n * When _all_ collateral tokens default for a target unit, only then is the basket allowed to fall\n *   in terms of target unit amounts. The basket is considered defaulted in this case.\n */\ninterface IBasketHandler is IComponent {\n    /// Emitted when the prime basket is set\n    /// @param erc20s The collateral tokens for the prime basket\n    /// @param targetAmts {target/BU} A list of quantities of target unit per basket unit\n    /// @param targetNames Each collateral token's targetName\n    event PrimeBasketSet(IERC20[] erc20s, uint192[] targetAmts, bytes32[] targetNames);\n\n    /// Emitted when the reference basket is set\n    /// @param nonce {basketNonce} The basket nonce\n    /// @param erc20s The list of collateral tokens in the reference basket\n    /// @param refAmts {ref/BU} The reference amounts of the basket collateral tokens\n    /// @param disabled True when the list of erc20s + refAmts may not be correct\n    event BasketSet(uint256 indexed nonce, IERC20[] erc20s, uint192[] refAmts, bool disabled);\n\n    /// Emitted when a backup config is set for a target unit\n    /// @param targetName The name of the target unit as a bytes32\n    /// @param max The max number to use from `erc20s`\n    /// @param erc20s The set of backup collateral tokens\n    event BackupConfigSet(bytes32 indexed targetName, uint256 max, IERC20[] erc20s);\n\n    /// Emitted when the warmup period is changed\n    /// @param oldVal The old warmup period\n    /// @param newVal The new warmup period\n    event WarmupPeriodSet(uint48 oldVal, uint48 newVal);\n\n    /// Emitted when the status of a basket has changed\n    /// @param oldStatus The previous basket status\n    /// @param newStatus The new basket status\n    event BasketStatusChanged(CollateralStatus oldStatus, CollateralStatus newStatus);\n\n    // Initialization\n    function init(IMain main_, uint48 warmupPeriod_) external;\n\n    /// Set the prime basket\n    /// @param erc20s The collateral tokens for the new prime basket\n    /// @param targetAmts The target amounts (in) {target/BU} for the new prime basket\n    ///                   required range: 1e9 values; absolute range irrelevant.\n    /// @custom:governance\n    function setPrimeBasket(IERC20[] memory erc20s, uint192[] memory targetAmts) external;\n\n    /// Set the backup configuration for a given target\n    /// @param targetName The name of the target as a bytes32\n    /// @param max The maximum number of collateral tokens to use from this target\n    ///            Required range: 1-255\n    /// @param erc20s A list of ordered backup collateral tokens\n    /// @custom:governance\n    function setBackupConfig(\n        bytes32 targetName,\n        uint256 max,\n        IERC20[] calldata erc20s\n    ) external;\n\n    /// Default the basket in order to schedule a basket refresh\n    /// @custom:protected\n    function disableBasket() external;\n\n    /// Governance-controlled setter to cause a basket switch explicitly\n    /// @custom:governance\n    /// @custom:interaction\n    function refreshBasket() external;\n\n    /// Track the basket status changes\n    /// @custom:refresher\n    function trackStatus() external;\n\n    /// @return If the BackingManager has sufficient collateral to redeem the entire RToken supply\n    function fullyCollateralized() external view returns (bool);\n\n    /// @return status The worst CollateralStatus of all collateral in the basket\n    function status() external view returns (CollateralStatus status);\n\n    /// @return If the basket is ready to issue and trade\n    function isReady() external view returns (bool);\n\n    /// @param erc20 The ERC20 token contract for the asset\n    /// @return {tok/BU} The whole token quantity of token in the reference basket\n    /// Returns 0 if erc20 is not registered or not in the basket\n    /// Returns FIX_MAX (in lieu of +infinity) if Collateral.refPerTok() is 0.\n    /// Otherwise, returns (token's basket.refAmts / token's Collateral.refPerTok())\n    function quantity(IERC20 erc20) external view returns (uint192);\n\n    /// Like quantity(), but unsafe because it DOES NOT CONFIRM THAT THE ASSET IS CORRECT\n    /// @param erc20 The ERC20 token contract for the asset\n    /// @param asset The registered asset plugin contract for the erc20\n    /// @return {tok/BU} The whole token quantity of token in the reference basket\n    /// Returns 0 if erc20 is not registered or not in the basket\n    /// Returns FIX_MAX (in lieu of +infinity) if Collateral.refPerTok() is 0.\n    /// Otherwise, returns (token's basket.refAmts / token's Collateral.refPerTok())\n    function quantityUnsafe(IERC20 erc20, IAsset asset) external view returns (uint192);\n\n    /// @param amount {BU}\n    /// @return erc20s The addresses of the ERC20 tokens in the reference basket\n    /// @return quantities {qTok} The quantity of each ERC20 token to issue `amount` baskets\n    function quote(uint192 amount, RoundingMode rounding)\n        external\n        view\n        returns (address[] memory erc20s, uint256[] memory quantities);\n\n    /// Return the redemption value of `amount` BUs for a linear combination of historical baskets\n    /// @param basketNonces An array of basket nonces to do redemption from\n    /// @param portions {1} An array of Fix quantities that must add up to FIX_ONE\n    /// @param amount {BU}\n    /// @return erc20s The backing collateral erc20s\n    /// @return quantities {qTok} ERC20 token quantities equal to `amount` BUs\n    function quoteCustomRedemption(\n        uint48[] memory basketNonces,\n        uint192[] memory portions,\n        uint192 amount\n    ) external view returns (address[] memory erc20s, uint256[] memory quantities);\n\n    /// @return top {BU} The number of partial basket units: e.g max(coll.map((c) => c.balAsBUs())\n    ///         bottom {BU} The number of whole basket units held by the account\n    function basketsHeldBy(address account) external view returns (BasketRange memory);\n\n    /// Should not revert\n    /// @return low {UoA/BU} The lower end of the price estimate\n    /// @return high {UoA/BU} The upper end of the price estimate\n    function price() external view returns (uint192 low, uint192 high);\n\n    /// Should not revert\n    /// lotLow should be nonzero if a BU could be worth selling\n    /// @return lotLow {UoA/tok} The lower end of the lot price estimate\n    /// @return lotHigh {UoA/tok} The upper end of the lot price estimate\n    function lotPrice() external view returns (uint192 lotLow, uint192 lotHigh);\n\n    /// @return timestamp The timestamp at which the basket was last set\n    function timestamp() external view returns (uint48);\n\n    /// @return The current basket nonce, regardless of status\n    function nonce() external view returns (uint48);\n}\n\ninterface TestIBasketHandler is IBasketHandler {\n    function warmupPeriod() external view returns (uint48);\n\n    function setWarmupPeriod(uint48 val) external;\n}\n"
      },
      "contracts/interfaces/IBroker.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"./IAsset.sol\";\nimport \"./IComponent.sol\";\nimport \"./IGnosis.sol\";\nimport \"./ITrade.sol\";\n\nenum TradeKind {\n    DUTCH_AUCTION,\n    BATCH_AUCTION\n}\n\n/// Cache of all (lot) prices for a pair to prevent re-lookup\nstruct TradePrices {\n    uint192 sellLow; // {UoA/sellTok} can be 0\n    uint192 sellHigh; // {UoA/sellTok} should not be 0\n    uint192 buyLow; // {UoA/buyTok} should not be 0\n    uint192 buyHigh; // {UoA/buyTok} should not be 0 or FIX_MAX\n}\n\n/// The data format that describes a request for trade with the Broker\nstruct TradeRequest {\n    IAsset sell;\n    IAsset buy;\n    uint256 sellAmount; // {qSellTok}\n    uint256 minBuyAmount; // {qBuyTok}\n}\n\n/**\n * @title IBroker\n * @notice The Broker deploys oneshot Trade contracts for Traders and monitors\n *   the continued proper functioning of trading platforms.\n */\ninterface IBroker is IComponent {\n    event GnosisSet(IGnosis oldVal, IGnosis newVal);\n    event BatchTradeImplementationSet(ITrade oldVal, ITrade newVal);\n    event DutchTradeImplementationSet(ITrade oldVal, ITrade newVal);\n    event BatchAuctionLengthSet(uint48 oldVal, uint48 newVal);\n    event DutchAuctionLengthSet(uint48 oldVal, uint48 newVal);\n    event BatchTradeDisabledSet(bool prevVal, bool newVal);\n    event DutchTradeDisabledSet(IERC20Metadata indexed erc20, bool prevVal, bool newVal);\n\n    // Initialization\n    function init(\n        IMain main_,\n        IGnosis gnosis_,\n        ITrade batchTradeImplemention_,\n        uint48 batchAuctionLength_,\n        ITrade dutchTradeImplemention_,\n        uint48 dutchAuctionLength_\n    ) external;\n\n    /// Request a trade from the broker\n    /// @dev Requires setting an allowance in advance\n    /// @custom:interaction\n    function openTrade(\n        TradeKind kind,\n        TradeRequest memory req,\n        TradePrices memory prices\n    ) external returns (ITrade);\n\n    /// Only callable by one of the trading contracts the broker deploys\n    function reportViolation() external;\n\n    function batchTradeDisabled() external view returns (bool);\n\n    function dutchTradeDisabled(IERC20Metadata erc20) external view returns (bool);\n}\n\ninterface TestIBroker is IBroker {\n    function gnosis() external view returns (IGnosis);\n\n    function batchTradeImplementation() external view returns (ITrade);\n\n    function dutchTradeImplementation() external view returns (ITrade);\n\n    function batchAuctionLength() external view returns (uint48);\n\n    function dutchAuctionLength() external view returns (uint48);\n\n    function setGnosis(IGnosis newGnosis) external;\n\n    function setBatchTradeImplementation(ITrade newTradeImplementation) external;\n\n    function setBatchAuctionLength(uint48 newAuctionLength) external;\n\n    function setDutchTradeImplementation(ITrade newTradeImplementation) external;\n\n    function setDutchAuctionLength(uint48 newAuctionLength) external;\n\n    function enableBatchTrade() external;\n\n    function enableDutchTrade(IERC20Metadata erc20) external;\n\n    // only present on pre-3.0.0 Brokers; used by EasyAuction regression test\n    function disabled() external view returns (bool);\n}\n"
      },
      "contracts/interfaces/IComponent.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"./IMain.sol\";\nimport \"./IVersioned.sol\";\n\n/**\n * @title IComponent\n * @notice A Component is the central building block of all our system contracts. Components\n *   contain important state that must be migrated during upgrades, and they delegate\n *   their ownership to Main's owner.\n */\ninterface IComponent is IVersioned {\n    function main() external view returns (IMain);\n}\n"
      },
      "contracts/interfaces/IDistributor.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"./IComponent.sol\";\n\nuint256 constant MAX_DISTRIBUTION = 1e4; // 10,000\nuint8 constant MAX_DESTINATIONS = 100; // maximum number of RevenueShare destinations\n\nstruct RevenueShare {\n    uint16 rTokenDist; // {revShare} A value between [0, 10,000]\n    uint16 rsrDist; // {revShare} A value between [0, 10,000]\n}\n\n/// Assumes no more than 100 independent distributions.\nstruct RevenueTotals {\n    uint24 rTokenTotal; // {revShare}\n    uint24 rsrTotal; // {revShare}\n}\n\n/**\n * @title IDistributor\n * @notice The Distributor Component maintains a revenue distribution table that dictates\n *   how to divide revenue across the Furnace, StRSR, and any other destinations.\n */\ninterface IDistributor is IComponent {\n    /// Emitted when a distribution is set\n    /// @param dest The address set to receive the distribution\n    /// @param rTokenDist The distribution of RToken that should go to `dest`\n    /// @param rsrDist The distribution of RSR that should go to `dest`\n    event DistributionSet(address indexed dest, uint16 rTokenDist, uint16 rsrDist);\n\n    /// Emitted when revenue is distributed\n    /// @param erc20 The token being distributed, either RSR or the RToken itself\n    /// @param source The address providing the revenue\n    /// @param amount The amount of the revenue\n    event RevenueDistributed(IERC20 indexed erc20, address indexed source, uint256 amount);\n\n    // Initialization\n    function init(IMain main_, RevenueShare memory dist) external;\n\n    /// @custom:governance\n    function setDistribution(address dest, RevenueShare memory share) external;\n\n    /// Distribute the `erc20` token across all revenue destinations\n    /// Only callable by RevenueTraders\n    /// @custom:protected\n    function distribute(IERC20 erc20, uint256 amount) external;\n\n    /// @return revTotals The total of all  destinations\n    function totals() external view returns (RevenueTotals memory revTotals);\n}\n\ninterface TestIDistributor is IDistributor {\n    // solhint-disable-next-line func-name-mixedcase\n    function FURNACE() external view returns (address);\n\n    // solhint-disable-next-line func-name-mixedcase\n    function ST_RSR() external view returns (address);\n\n    /// @return rTokenDist The RToken distribution for the address\n    /// @return rsrDist The RSR distribution for the address\n    function distribution(address) external view returns (uint16 rTokenDist, uint16 rsrDist);\n}\n"
      },
      "contracts/interfaces/IFurnace.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"../libraries/Fixed.sol\";\nimport \"./IComponent.sol\";\n\n/**\n * @title IFurnace\n * @notice A helper contract to burn RTokens slowly and permisionlessly.\n */\ninterface IFurnace is IComponent {\n    // Initialization\n    function init(IMain main_, uint192 ratio_) external;\n\n    /// Emitted when the melting ratio is changed\n    /// @param oldRatio The old ratio\n    /// @param newRatio The new ratio\n    event RatioSet(uint192 oldRatio, uint192 newRatio);\n\n    function ratio() external view returns (uint192);\n\n    ///    Needed value range: [0, 1], granularity 1e-9\n    /// @custom:governance\n    function setRatio(uint192) external;\n\n    /// Performs any RToken melting that has vested since the last payout.\n    /// @custom:refresher\n    function melt() external;\n}\n\ninterface TestIFurnace is IFurnace {\n    function lastPayout() external view returns (uint256);\n\n    function lastPayoutBal() external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IGnosis.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nstruct GnosisAuctionData {\n    IERC20 auctioningToken;\n    IERC20 biddingToken;\n    uint256 orderCancellationEndDate;\n    uint256 auctionEndDate;\n    bytes32 initialAuctionOrder;\n    uint256 minimumBiddingAmountPerOrder;\n    uint256 interimSumBidAmount;\n    bytes32 interimOrder;\n    bytes32 clearingPriceOrder;\n    uint96 volumeClearingPriceOrder;\n    bool minFundingThresholdNotReached;\n    bool isAtomicClosureAllowed;\n    uint256 feeNumerator;\n    uint256 minFundingThreshold;\n}\n\n/// The relevant portion of the interface of the live Gnosis EasyAuction contract\n/// https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\ninterface IGnosis {\n    function initiateAuction(\n        IERC20 auctioningToken,\n        IERC20 biddingToken,\n        uint256 orderCancellationEndDate,\n        uint256 auctionEndDate,\n        uint96 auctionedSellAmount,\n        uint96 minBuyAmount,\n        uint256 minimumBiddingAmountPerOrder,\n        uint256 minFundingThreshold,\n        bool isAtomicClosureAllowed,\n        address accessManagerContract,\n        bytes memory accessManagerContractData\n    ) external returns (uint256 auctionId);\n\n    function auctionData(uint256 auctionId) external view returns (GnosisAuctionData memory);\n\n    /// @param auctionId The external auction id\n    /// @dev See here for decoding: https://git.io/JMang\n    /// @return encodedOrder The order, encoded in a bytes 32\n    function settleAuction(uint256 auctionId) external returns (bytes32 encodedOrder);\n\n    /// @return The numerator over a 1000-valued denominator\n    function feeNumerator() external returns (uint256);\n}\n"
      },
      "contracts/interfaces/IMain.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"./IAssetRegistry.sol\";\nimport \"./IBasketHandler.sol\";\nimport \"./IBackingManager.sol\";\nimport \"./IBroker.sol\";\nimport \"./IGnosis.sol\";\nimport \"./IFurnace.sol\";\nimport \"./IDistributor.sol\";\nimport \"./IRToken.sol\";\nimport \"./IRevenueTrader.sol\";\nimport \"./IStRSR.sol\";\nimport \"./ITrading.sol\";\nimport \"./IVersioned.sol\";\n\n// === Auth roles ===\n\nbytes32 constant OWNER = bytes32(bytes(\"OWNER\"));\nbytes32 constant SHORT_FREEZER = bytes32(bytes(\"SHORT_FREEZER\"));\nbytes32 constant LONG_FREEZER = bytes32(bytes(\"LONG_FREEZER\"));\nbytes32 constant PAUSER = bytes32(bytes(\"PAUSER\"));\n\n/**\n * Main is a central hub that maintains a list of Component contracts.\n *\n * Components:\n *   - perform a specific function\n *   - defer auth to Main\n *   - usually (but not always) contain sizeable state that require a proxy\n */\nstruct Components {\n    // Definitely need proxy\n    IRToken rToken;\n    IStRSR stRSR;\n    IAssetRegistry assetRegistry;\n    IBasketHandler basketHandler;\n    IBackingManager backingManager;\n    IDistributor distributor;\n    IFurnace furnace;\n    IBroker broker;\n    IRevenueTrader rsrTrader;\n    IRevenueTrader rTokenTrader;\n}\n\ninterface IAuth is IAccessControlUpgradeable {\n    /// Emitted when `unfreezeAt` is changed\n    /// @param oldVal The old value of `unfreezeAt`\n    /// @param newVal The new value of `unfreezeAt`\n    event UnfreezeAtSet(uint48 oldVal, uint48 newVal);\n\n    /// Emitted when the short freeze duration governance param is changed\n    /// @param oldDuration The old short freeze duration\n    /// @param newDuration The new short freeze duration\n    event ShortFreezeDurationSet(uint48 oldDuration, uint48 newDuration);\n\n    /// Emitted when the long freeze duration governance param is changed\n    /// @param oldDuration The old long freeze duration\n    /// @param newDuration The new long freeze duration\n    event LongFreezeDurationSet(uint48 oldDuration, uint48 newDuration);\n\n    /// Emitted when the system is paused or unpaused for trading\n    /// @param oldVal The old value of `tradingPaused`\n    /// @param newVal The new value of `tradingPaused`\n    event TradingPausedSet(bool oldVal, bool newVal);\n\n    /// Emitted when the system is paused or unpaused for issuance\n    /// @param oldVal The old value of `issuancePaused`\n    /// @param newVal The new value of `issuancePaused`\n    event IssuancePausedSet(bool oldVal, bool newVal);\n\n    /**\n     * Trading Paused: Disable everything except for OWNER actions, RToken.issue, RToken.redeem,\n     * StRSR.stake, and StRSR.payoutRewards\n     * Issuance Paused: Disable RToken.issue\n     * Frozen: Disable everything except for OWNER actions + StRSR.stake (for governance)\n     */\n\n    function tradingPausedOrFrozen() external view returns (bool);\n\n    function issuancePausedOrFrozen() external view returns (bool);\n\n    function frozen() external view returns (bool);\n\n    function shortFreeze() external view returns (uint48);\n\n    function longFreeze() external view returns (uint48);\n\n    // ====\n\n    // onlyRole(OWNER)\n    function freezeForever() external;\n\n    // onlyRole(SHORT_FREEZER)\n    function freezeShort() external;\n\n    // onlyRole(LONG_FREEZER)\n    function freezeLong() external;\n\n    // onlyRole(OWNER)\n    function unfreeze() external;\n\n    function pauseTrading() external;\n\n    function unpauseTrading() external;\n\n    function pauseIssuance() external;\n\n    function unpauseIssuance() external;\n}\n\ninterface IComponentRegistry {\n    // === Component setters/getters ===\n\n    event RTokenSet(IRToken indexed oldVal, IRToken indexed newVal);\n\n    function rToken() external view returns (IRToken);\n\n    event StRSRSet(IStRSR oldVal, IStRSR newVal);\n\n    function stRSR() external view returns (IStRSR);\n\n    event AssetRegistrySet(IAssetRegistry oldVal, IAssetRegistry newVal);\n\n    function assetRegistry() external view returns (IAssetRegistry);\n\n    event BasketHandlerSet(IBasketHandler oldVal, IBasketHandler newVal);\n\n    function basketHandler() external view returns (IBasketHandler);\n\n    event BackingManagerSet(IBackingManager oldVal, IBackingManager newVal);\n\n    function backingManager() external view returns (IBackingManager);\n\n    event DistributorSet(IDistributor oldVal, IDistributor newVal);\n\n    function distributor() external view returns (IDistributor);\n\n    event RSRTraderSet(IRevenueTrader oldVal, IRevenueTrader newVal);\n\n    function rsrTrader() external view returns (IRevenueTrader);\n\n    event RTokenTraderSet(IRevenueTrader oldVal, IRevenueTrader newVal);\n\n    function rTokenTrader() external view returns (IRevenueTrader);\n\n    event FurnaceSet(IFurnace oldVal, IFurnace newVal);\n\n    function furnace() external view returns (IFurnace);\n\n    event BrokerSet(IBroker oldVal, IBroker newVal);\n\n    function broker() external view returns (IBroker);\n}\n\n/**\n * @title IMain\n * @notice The central hub for the entire system. Maintains components and an owner singleton role\n */\ninterface IMain is IVersioned, IAuth, IComponentRegistry {\n    function poke() external; // not used in p1\n\n    // === Initialization ===\n\n    event MainInitialized();\n\n    function init(\n        Components memory components,\n        IERC20 rsr_,\n        uint48 shortFreeze_,\n        uint48 longFreeze_\n    ) external;\n\n    function rsr() external view returns (IERC20);\n}\n\ninterface TestIMain is IMain {\n    /// @custom:governance\n    function setShortFreeze(uint48) external;\n\n    /// @custom:governance\n    function setLongFreeze(uint48) external;\n\n    function shortFreeze() external view returns (uint48);\n\n    function longFreeze() external view returns (uint48);\n\n    function longFreezes(address account) external view returns (uint256);\n\n    function tradingPaused() external view returns (bool);\n\n    function issuancePaused() external view returns (bool);\n}\n"
      },
      "contracts/interfaces/IRevenueTrader.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"./IBroker.sol\";\nimport \"./IComponent.sol\";\nimport \"./ITrading.sol\";\n\n/**\n * @title IRevenueTrader\n * @notice The RevenueTrader is an extension of the trading mixin that trades all\n *   assets at its address for a single target asset. There are two runtime instances\n *   of the RevenueTrader, 1 for RToken and 1 for RSR.\n */\ninterface IRevenueTrader is IComponent, ITrading {\n    // Initialization\n    function init(\n        IMain main_,\n        IERC20 tokenToBuy_,\n        uint192 maxTradeSlippage_,\n        uint192 minTradeVolume_\n    ) external;\n\n    /// Distribute tokenToBuy to its destinations\n    /// @dev Special-case of manageTokens()\n    /// @custom:interaction\n    function distributeTokenToBuy() external;\n\n    /// Return registered ERC20s to the BackingManager if distribution for tokenToBuy is 0\n    /// @custom:interaction\n    function returnTokens(IERC20[] memory erc20s) external;\n\n    /// Process some number of tokens\n    /// If the tokenToBuy is included in erc20s, RevenueTrader will distribute it at end of the tx\n    /// @param erc20s The ERC20s to manage; can be tokenToBuy or anything registered\n    /// @param kinds The kinds of auctions to launch: DUTCH_AUCTION | BATCH_AUCTION\n    /// @custom:interaction\n    function manageTokens(IERC20[] memory erc20s, TradeKind[] memory kinds) external;\n\n    function tokenToBuy() external view returns (IERC20);\n}\n\n// solhint-disable-next-line no-empty-blocks\ninterface TestIRevenueTrader is IRevenueTrader, TestITrading {\n\n}\n"
      },
      "contracts/interfaces/IRewardable.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"./IComponent.sol\";\nimport \"./IMain.sol\";\n\n/**\n * @title IRewardable\n * @notice A simple interface mixin to support claiming of rewards.\n */\ninterface IRewardable {\n    /// Emitted whenever a reward token balance is claimed\n    event RewardsClaimed(IERC20 indexed erc20, uint256 amount);\n\n    /// Claim rewards earned by holding a balance of the ERC20 token\n    /// Must emit `RewardsClaimed` for each token rewards are claimed for\n    /// @custom:interaction\n    function claimRewards() external;\n}\n\n/**\n * @title IRewardableComponent\n * @notice A simple interface mixin to support claiming of rewards.\n */\ninterface IRewardableComponent is IRewardable {\n    /// Claim rewards for a single ERC20\n    /// Must emit `RewardsClaimed` for each token rewards are claimed for\n    /// @custom:interaction\n    function claimRewardsSingle(IERC20 erc20) external;\n}\n"
      },
      "contracts/interfaces/IRToken.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol\";\n// solhint-disable-next-line max-line-length\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol\";\nimport \"../libraries/Fixed.sol\";\nimport \"../libraries/Throttle.sol\";\nimport \"./IAsset.sol\";\nimport \"./IComponent.sol\";\nimport \"./IMain.sol\";\nimport \"./IRewardable.sol\";\n\n/**\n * @title IRToken\n * @notice An RToken is an ERC20 that is permissionlessly issuable/redeemable and tracks an\n *   exchange rate against a single unit: baskets, or {BU} in our type notation.\n */\ninterface IRToken is IComponent, IERC20MetadataUpgradeable, IERC20PermitUpgradeable {\n    /// Emitted when an issuance of RToken occurs, whether it occurs via slow minting or not\n    /// @param issuer The address holding collateral tokens\n    /// @param recipient The address of the recipient of the RTokens\n    /// @param amount The quantity of RToken being issued\n    /// @param baskets The corresponding number of baskets\n    event Issuance(\n        address indexed issuer,\n        address indexed recipient,\n        uint256 amount,\n        uint192 baskets\n    );\n\n    /// Emitted when a redemption of RToken occurs\n    /// @param redeemer The address holding RToken\n    /// @param recipient The address of the account receiving the backing collateral tokens\n    /// @param amount The quantity of RToken being redeemed\n    /// @param baskets The corresponding number of baskets\n    /// @param amount {qRTok} The amount of RTokens canceled\n    event Redemption(\n        address indexed redeemer,\n        address indexed recipient,\n        uint256 amount,\n        uint192 baskets\n    );\n\n    /// Emitted when the number of baskets needed changes\n    /// @param oldBasketsNeeded Previous number of baskets units needed\n    /// @param newBasketsNeeded New number of basket units needed\n    event BasketsNeededChanged(uint192 oldBasketsNeeded, uint192 newBasketsNeeded);\n\n    /// Emitted when RToken is melted, i.e the RToken supply is decreased but basketsNeeded is not\n    /// @param amount {qRTok}\n    event Melted(uint256 amount);\n\n    /// Emitted when issuance SupplyThrottle params are set\n    event IssuanceThrottleSet(ThrottleLib.Params oldVal, ThrottleLib.Params newVal);\n\n    /// Emitted when redemption SupplyThrottle params are set\n    event RedemptionThrottleSet(ThrottleLib.Params oldVal, ThrottleLib.Params newVal);\n\n    // Initialization\n    function init(\n        IMain main_,\n        string memory name_,\n        string memory symbol_,\n        string memory mandate_,\n        ThrottleLib.Params calldata issuanceThrottleParams,\n        ThrottleLib.Params calldata redemptionThrottleParams\n    ) external;\n\n    /// Issue an RToken with basket collateral\n    /// @param amount {qRTok} The quantity of RToken to issue\n    /// @custom:interaction\n    function issue(uint256 amount) external;\n\n    /// Issue an RToken with basket collateral, to a particular recipient\n    /// @param recipient The address to receive the issued RTokens\n    /// @param amount {qRTok} The quantity of RToken to issue\n    /// @custom:interaction\n    function issueTo(address recipient, uint256 amount) external;\n\n    /// Redeem RToken for basket collateral\n    /// @dev Use redeemCustom for non-current baskets\n    /// @param amount {qRTok} The quantity {qRToken} of RToken to redeem\n    /// @custom:interaction\n    function redeem(uint256 amount) external;\n\n    /// Redeem RToken for basket collateral to a particular recipient\n    /// @dev Use redeemCustom for non-current baskets\n    /// @param recipient The address to receive the backing collateral tokens\n    /// @param amount {qRTok} The quantity {qRToken} of RToken to redeem\n    /// @custom:interaction\n    function redeemTo(address recipient, uint256 amount) external;\n\n    /// Redeem RToken for a linear combination of historical baskets, to a particular recipient\n    /// @dev Allows partial redemptions up to the minAmounts\n    /// @param recipient The address to receive the backing collateral tokens\n    /// @param amount {qRTok} The quantity {qRToken} of RToken to redeem\n    /// @param basketNonces An array of basket nonces to do redemption from\n    /// @param portions {1} An array of Fix quantities that must add up to FIX_ONE\n    /// @param expectedERC20sOut An array of ERC20s expected out\n    /// @param minAmounts {qTok} The minimum ERC20 quantities the caller should receive\n    /// @custom:interaction\n    function redeemCustom(\n        address recipient,\n        uint256 amount,\n        uint48[] memory basketNonces,\n        uint192[] memory portions,\n        address[] memory expectedERC20sOut,\n        uint256[] memory minAmounts\n    ) external;\n\n    /// Mint an amount of RToken equivalent to baskets BUs, scaling basketsNeeded up\n    /// Callable only by BackingManager\n    /// @param baskets {BU} The number of baskets to mint RToken for\n    /// @custom:protected\n    function mint(uint192 baskets) external;\n\n    /// Melt a quantity of RToken from the caller's account\n    /// @param amount {qRTok} The amount to be melted\n    /// @custom:protected\n    function melt(uint256 amount) external;\n\n    /// Burn an amount of RToken from caller's account and scale basketsNeeded down\n    /// Callable only by BackingManager\n    /// @custom:protected\n    function dissolve(uint256 amount) external;\n\n    /// Set the number of baskets needed directly, callable only by the BackingManager\n    /// @param basketsNeeded {BU} The number of baskets to target\n    ///                      needed range: pretty interesting\n    /// @custom:protected\n    function setBasketsNeeded(uint192 basketsNeeded) external;\n\n    /// @return {BU} How many baskets are being targeted\n    function basketsNeeded() external view returns (uint192);\n\n    /// @return {qRTok} The maximum issuance that can be performed in the current block\n    function issuanceAvailable() external view returns (uint256);\n\n    /// @return {qRTok} The maximum redemption that can be performed in the current block\n    function redemptionAvailable() external view returns (uint256);\n}\n\ninterface TestIRToken is IRToken {\n    function setIssuanceThrottleParams(ThrottleLib.Params calldata) external;\n\n    function setRedemptionThrottleParams(ThrottleLib.Params calldata) external;\n\n    function issuanceThrottleParams() external view returns (ThrottleLib.Params memory);\n\n    function redemptionThrottleParams() external view returns (ThrottleLib.Params memory);\n\n    function increaseAllowance(address, uint256) external returns (bool);\n\n    function decreaseAllowance(address, uint256) external returns (bool);\n\n    function monetizeDonations(IERC20) external;\n}\n"
      },
      "contracts/interfaces/IRTokenOracle.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\n// RToken Oracle Interface\ninterface IRTokenOracle {\n    struct CachedOracleData {\n        uint192 cachedPrice; // {UoA/tok}\n        uint256 cachedAtTime; // {s}\n        uint48 cachedAtNonce; // {basketNonce}\n        uint48 cachedTradesOpen;\n        uint256 cachedTradesNonce; // {tradeNonce}\n    }\n\n    // @returns rTokenPrice {D18} {UoA/rTok} The price of the RToken, in UoA\n    function latestPrice() external returns (uint192 rTokenPrice, uint256 updatedAt);\n\n    // Force recalculate the price of the RToken\n    function forceUpdatePrice() external;\n}\n"
      },
      "contracts/interfaces/IStRSR.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol\";\n// solhint-disable-next-line max-line-length\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol\";\nimport \"../libraries/Fixed.sol\";\nimport \"./IComponent.sol\";\nimport \"./IMain.sol\";\n\n/**\n * @title IStRSR\n * @notice An ERC20 token representing shares of the RSR over-collateralization pool.\n *\n * StRSR permits the BackingManager to take RSR in times of need. In return, the BackingManager\n * benefits the StRSR pool with RSR rewards purchased with a portion of its revenue.\n *\n * In the absence of collateral default or losses due to slippage, StRSR should have a\n * monotonically increasing exchange rate with respect to RSR, meaning that over time\n * StRSR is redeemable for more RSR. It is non-rebasing.\n */\ninterface IStRSR is IERC20MetadataUpgradeable, IERC20PermitUpgradeable, IComponent {\n    /// Emitted when RSR is staked\n    /// @param era The era at time of staking\n    /// @param staker The address of the staker\n    /// @param rsrAmount {qRSR} How much RSR was staked\n    /// @param stRSRAmount {qStRSR} How much stRSR was minted by this staking\n    event Staked(\n        uint256 indexed era,\n        address indexed staker,\n        uint256 rsrAmount,\n        uint256 stRSRAmount\n    );\n\n    /// Emitted when an unstaking is started\n    /// @param draftId The id of the draft.\n    /// @param draftEra The era of the draft.\n    /// @param staker The address of the unstaker\n    ///   The triple (staker, draftEra, draftId) is a unique ID\n    /// @param rsrAmount {qRSR} How much RSR this unstaking will be worth, absent seizures\n    /// @param stRSRAmount {qStRSR} How much stRSR was burned by this unstaking\n    event UnstakingStarted(\n        uint256 indexed draftId,\n        uint256 indexed draftEra,\n        address indexed staker,\n        uint256 rsrAmount,\n        uint256 stRSRAmount,\n        uint256 availableAt\n    );\n\n    /// Emitted when RSR is unstaked\n    /// @param firstId The beginning of the range of draft IDs withdrawn in this transaction\n    /// @param endId The end of range of draft IDs withdrawn in this transaction\n    ///   (ID i was withdrawn if firstId <= i < endId)\n    /// @param draftEra The era of the draft.\n    ///   The triple (staker, draftEra, id) is a unique ID among drafts\n    /// @param staker The address of the unstaker\n\n    /// @param rsrAmount {qRSR} How much RSR this unstaking was worth\n    event UnstakingCompleted(\n        uint256 indexed firstId,\n        uint256 indexed endId,\n        uint256 draftEra,\n        address indexed staker,\n        uint256 rsrAmount\n    );\n\n    /// Emitted when RSR unstaking is cancelled\n    /// @param firstId The beginning of the range of draft IDs withdrawn in this transaction\n    /// @param endId The end of range of draft IDs withdrawn in this transaction\n    ///   (ID i was withdrawn if firstId <= i < endId)\n    /// @param draftEra The era of the draft.\n    ///   The triple (staker, draftEra, id) is a unique ID among drafts\n    /// @param staker The address of the unstaker\n\n    /// @param rsrAmount {qRSR} How much RSR this unstaking was worth\n    event UnstakingCancelled(\n        uint256 indexed firstId,\n        uint256 indexed endId,\n        uint256 draftEra,\n        address indexed staker,\n        uint256 rsrAmount\n    );\n\n    /// Emitted whenever the exchange rate changes\n    event ExchangeRateSet(uint192 oldVal, uint192 newVal);\n\n    /// Emitted whenever RSR are paids out\n    event RewardsPaid(uint256 rsrAmt);\n\n    /// Emitted if all the RSR in the staking pool is seized and all balances are reset to zero.\n    event AllBalancesReset(uint256 indexed newEra);\n    /// Emitted if all the RSR in the unstakin pool is seized, and all ongoing unstaking is voided.\n    event AllUnstakingReset(uint256 indexed newEra);\n\n    event UnstakingDelaySet(uint48 oldVal, uint48 newVal);\n    event RewardRatioSet(uint192 oldVal, uint192 newVal);\n    event WithdrawalLeakSet(uint192 oldVal, uint192 newVal);\n\n    // Initialization\n    function init(\n        IMain main_,\n        string memory name_,\n        string memory symbol_,\n        uint48 unstakingDelay_,\n        uint192 rewardRatio_,\n        uint192 withdrawalLeak_\n    ) external;\n\n    /// Gather and payout rewards from rsrTrader\n    /// @custom:interaction\n    function payoutRewards() external;\n\n    /// Stakes an RSR `amount` on the corresponding RToken to earn yield and over-collateralized\n    /// the system\n    /// @param amount {qRSR}\n    /// @custom:interaction\n    function stake(uint256 amount) external;\n\n    /// Begins a delayed unstaking for `amount` stRSR\n    /// @param amount {qStRSR}\n    /// @custom:interaction\n    function unstake(uint256 amount) external;\n\n    /// Complete delayed unstaking for the account, up to (but not including!) `endId`\n    /// @custom:interaction\n    function withdraw(address account, uint256 endId) external;\n\n    /// Cancel unstaking for the account, up to (but not including!) `endId`\n    /// @custom:interaction\n    function cancelUnstake(uint256 endId) external;\n\n    /// Seize RSR, only callable by main.backingManager()\n    /// @custom:protected\n    function seizeRSR(uint256 amount) external;\n\n    /// Reset all stakes and advance era\n    /// @custom:governance\n    function resetStakes() external;\n\n    /// Return the maximum valid value of endId such that withdraw(endId) should immediately work\n    function endIdForWithdraw(address account) external view returns (uint256 endId);\n\n    /// @return {qRSR/qStRSR} The exchange rate between RSR and StRSR\n    function exchangeRate() external view returns (uint192);\n}\n\ninterface TestIStRSR is IStRSR {\n    function rewardRatio() external view returns (uint192);\n\n    function setRewardRatio(uint192) external;\n\n    function unstakingDelay() external view returns (uint48);\n\n    function setUnstakingDelay(uint48) external;\n\n    function withdrawalLeak() external view returns (uint192);\n\n    function setWithdrawalLeak(uint192) external;\n\n    function increaseAllowance(address, uint256) external returns (bool);\n\n    function decreaseAllowance(address, uint256) external returns (bool);\n\n    /// @return {qStRSR/qRSR} The exchange rate between StRSR and RSR\n    function exchangeRate() external view returns (uint192);\n}\n"
      },
      "contracts/interfaces/ITrade.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"./IBroker.sol\";\n\nenum TradeStatus {\n    NOT_STARTED, // before init()\n    OPEN, // after init() and before settle()\n    CLOSED, // after settle()\n    // === Intermediate-tx state ===\n    PENDING // during init() or settle() (reentrancy protection)\n}\n\n/**\n * Simple generalized trading interface for all Trade contracts to obey\n *\n * Usage: if (canSettle()) settle()\n */\ninterface ITrade {\n    /// Complete the trade and transfer tokens back to the origin trader\n    /// @return soldAmt {qSellTok} The quantity of tokens sold\n    /// @return boughtAmt {qBuyTok} The quantity of tokens bought\n    function settle() external returns (uint256 soldAmt, uint256 boughtAmt);\n\n    function sell() external view returns (IERC20Metadata);\n\n    function buy() external view returns (IERC20Metadata);\n\n    /// @return The timestamp at which the trade is projected to become settle-able\n    function endTime() external view returns (uint48);\n\n    /// @return True if the trade can be settled\n    /// @dev Should be guaranteed to be true eventually as an invariant\n    function canSettle() external view returns (bool);\n\n    /// @return TradeKind.DUTCH_AUCTION or TradeKind.BATCH_AUCTION\n    // solhint-disable-next-line func-name-mixedcase\n    function KIND() external view returns (TradeKind);\n}\n"
      },
      "contracts/interfaces/ITrading.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../libraries/Fixed.sol\";\nimport \"./IAsset.sol\";\nimport \"./IComponent.sol\";\nimport \"./ITrade.sol\";\nimport \"./IRewardable.sol\";\n\n/**\n * @title ITrading\n * @notice Common events and refresher function for all Trading contracts\n */\ninterface ITrading is IComponent, IRewardableComponent {\n    event MaxTradeSlippageSet(uint192 oldVal, uint192 newVal);\n    event MinTradeVolumeSet(uint192 oldVal, uint192 newVal);\n\n    /// Emitted when a trade is started\n    /// @param trade The one-time-use trade contract that was just deployed\n    /// @param sell The token to sell\n    /// @param buy The token to buy\n    /// @param sellAmount {qSellTok} The quantity of the selling token\n    /// @param minBuyAmount {qBuyTok} The minimum quantity of the buying token to accept\n    event TradeStarted(\n        ITrade indexed trade,\n        IERC20 indexed sell,\n        IERC20 indexed buy,\n        uint256 sellAmount,\n        uint256 minBuyAmount\n    );\n\n    /// Emitted after a trade ends\n    /// @param trade The one-time-use trade contract\n    /// @param sell The token to sell\n    /// @param buy The token to buy\n    /// @param sellAmount {qSellTok} The quantity of the token sold\n    /// @param buyAmount {qBuyTok} The quantity of the token bought\n    event TradeSettled(\n        ITrade indexed trade,\n        IERC20 indexed sell,\n        IERC20 indexed buy,\n        uint256 sellAmount,\n        uint256 buyAmount\n    );\n\n    /// Settle a single trade, expected to be used with multicall for efficient mass settlement\n    /// @param sell The sell token in the trade\n    /// @return The trade settled\n    /// @custom:refresher\n    function settleTrade(IERC20 sell) external returns (ITrade);\n\n    /// @return {%} The maximum trade slippage acceptable\n    function maxTradeSlippage() external view returns (uint192);\n\n    /// @return {UoA} The minimum trade volume in UoA, applies to all assets\n    function minTradeVolume() external view returns (uint192);\n\n    /// @return The ongoing trade for a sell token, or the zero address\n    function trades(IERC20 sell) external view returns (ITrade);\n\n    /// @return The number of ongoing trades open\n    function tradesOpen() external view returns (uint48);\n\n    /// @return The number of total trades ever opened\n    function tradesNonce() external view returns (uint256);\n}\n\ninterface TestITrading is ITrading {\n    /// @custom:governance\n    function setMaxTradeSlippage(uint192 val) external;\n\n    /// @custom:governance\n    function setMinTradeVolume(uint192 val) external;\n}\n"
      },
      "contracts/interfaces/IVersioned.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\ninterface IVersioned {\n    function version() external view returns (string memory);\n}\n"
      },
      "contracts/libraries/Fixed.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\n// solhint-disable func-name-mixedcase func-visibility\npragma solidity ^0.8.19;\n\n/// @title FixedPoint, a fixed-point arithmetic library defining the custom type uint192\n/// @author Matt Elder <matt.elder@reserve.org> and the Reserve Team <https://reserve.org>\n\n/** The logical type `uint192 ` is a 192 bit value, representing an 18-decimal Fixed-point\n    fractional value.  This is what's described in the Solidity documentation as\n    \"fixed192x18\" -- a value represented by 192 bits, that makes 18 digits available to\n    the right of the decimal point.\n\n    The range of values that uint192 can represent is about [-1.7e20, 1.7e20].\n    Unless a function explicitly says otherwise, it will fail on overflow.\n    To be clear, the following should hold:\n    toFix(0) == 0\n    toFix(1) == 1e18\n*/\n\n// Analysis notes:\n//   Every function should revert iff its result is out of bounds.\n//   Unless otherwise noted, when a rounding mode is given, that mode is applied to\n//     a single division that may happen as the last step in the computation.\n//   Unless otherwise noted, when a rounding mode is *not* given but is needed, it's FLOOR.\n//   For each, we comment:\n//   - @return is the value expressed  in \"value space\", where uint192(1e18) \"is\" 1.0\n//   - as-ints: is the value expressed in \"implementation space\", where uint192(1e18) \"is\" 1e18\n//   The \"@return\" expression is suitable for actually using the library\n//   The \"as-ints\" expression is suitable for testing\n\n// A uint value passed to this library was out of bounds for uint192 operations\nerror UIntOutOfBounds();\nbytes32 constant UIntOutofBoundsHash = keccak256(abi.encodeWithSignature(\"UIntOutOfBounds()\"));\n\n// Used by P1 implementation for easier casting\nuint256 constant FIX_ONE_256 = 1e18;\nuint8 constant FIX_DECIMALS = 18;\n\n// If a particular uint192 is represented by the uint192 n, then the uint192 represents the\n// value n/FIX_SCALE.\nuint64 constant FIX_SCALE = 1e18;\n\n// FIX_SCALE Squared:\nuint128 constant FIX_SCALE_SQ = 1e36;\n\n// The largest integer that can be converted to uint192 .\n// This is a bit bigger than 3.1e39\nuint192 constant FIX_MAX_INT = type(uint192).max / FIX_SCALE;\n\nuint192 constant FIX_ZERO = 0; // The uint192 representation of zero.\nuint192 constant FIX_ONE = FIX_SCALE; // The uint192 representation of one.\nuint192 constant FIX_MAX = type(uint192).max; // The largest uint192. (Not an integer!)\nuint192 constant FIX_MIN = 0; // The smallest uint192.\n\n/// An enum that describes a rounding approach for converting to ints\nenum RoundingMode {\n    FLOOR, // Round towards zero\n    ROUND, // Round to the nearest int\n    CEIL // Round away from zero\n}\n\nRoundingMode constant FLOOR = RoundingMode.FLOOR;\nRoundingMode constant ROUND = RoundingMode.ROUND;\nRoundingMode constant CEIL = RoundingMode.CEIL;\n\n/* @dev Solidity 0.8.x only allows you to change one of type or size per type conversion.\n   Thus, all the tedious-looking double conversions like uint256(uint256 (foo))\n   See: https://docs.soliditylang.org/en/v0.8.17/080-breaking-changes.html#new-restrictions\n */\n\n/// Explicitly convert a uint256 to a uint192. Revert if the input is out of bounds.\nfunction _safeWrap(uint256 x) pure returns (uint192) {\n    if (FIX_MAX < x) revert UIntOutOfBounds();\n    return uint192(x);\n}\n\n/// Convert a uint to its Fix representation.\n/// @return x\n// as-ints: x * 1e18\nfunction toFix(uint256 x) pure returns (uint192) {\n    return _safeWrap(x * FIX_SCALE);\n}\n\n/// Convert a uint to its fixed-point representation, and left-shift its value `shiftLeft`\n/// decimal digits.\n/// @return x * 10**shiftLeft\n// as-ints: x * 10**(shiftLeft + 18)\nfunction shiftl_toFix(uint256 x, int8 shiftLeft) pure returns (uint192) {\n    return shiftl_toFix(x, shiftLeft, FLOOR);\n}\n\n/// @return x * 10**shiftLeft\n// as-ints: x * 10**(shiftLeft + 18)\nfunction shiftl_toFix(\n    uint256 x,\n    int8 shiftLeft,\n    RoundingMode rounding\n) pure returns (uint192) {\n    // conditions for avoiding overflow\n    if (x == 0) return 0;\n    if (shiftLeft <= -96) return (rounding == CEIL ? 1 : 0); // 0 < uint.max / 10**77 < 0.5\n    if (40 <= shiftLeft) revert UIntOutOfBounds(); // 10**56 < FIX_MAX < 10**57\n\n    shiftLeft += 18;\n\n    uint256 coeff = 10**abs(shiftLeft);\n    uint256 shifted = (shiftLeft >= 0) ? x * coeff : _divrnd(x, coeff, rounding);\n\n    return _safeWrap(shifted);\n}\n\n/// Divide a uint by a uint192, yielding a uint192\n/// This may also fail if the result is MIN_uint192! not fixing this for optimization's sake.\n/// @return x / y\n// as-ints: x * 1e36 / y\nfunction divFix(uint256 x, uint192 y) pure returns (uint192) {\n    // If we didn't have to worry about overflow, we'd just do `return x * 1e36 / _y`\n    // If it's safe to do this operation the easy way, do it:\n    if (x < uint256(type(uint256).max / FIX_SCALE_SQ)) {\n        return _safeWrap(uint256(x * FIX_SCALE_SQ) / y);\n    } else {\n        return _safeWrap(mulDiv256(x, FIX_SCALE_SQ, y));\n    }\n}\n\n/// Divide a uint by a uint, yielding a  uint192\n/// @return x / y\n// as-ints: x * 1e18 / y\nfunction divuu(uint256 x, uint256 y) pure returns (uint192) {\n    return _safeWrap(mulDiv256(FIX_SCALE, x, y));\n}\n\n/// @return min(x,y)\n// as-ints: min(x,y)\nfunction fixMin(uint192 x, uint192 y) pure returns (uint192) {\n    return x < y ? x : y;\n}\n\n/// @return max(x,y)\n// as-ints: max(x,y)\nfunction fixMax(uint192 x, uint192 y) pure returns (uint192) {\n    return x > y ? x : y;\n}\n\n/// @return absoluteValue(x,y)\n// as-ints: absoluteValue(x,y)\nfunction abs(int256 x) pure returns (uint256) {\n    return x < 0 ? uint256(-x) : uint256(x);\n}\n\n/// Divide two uints, returning a uint, using rounding mode `rounding`.\n/// @return numerator / divisor\n// as-ints: numerator / divisor\nfunction _divrnd(\n    uint256 numerator,\n    uint256 divisor,\n    RoundingMode rounding\n) pure returns (uint256) {\n    uint256 result = numerator / divisor;\n\n    if (rounding == FLOOR) return result;\n\n    if (rounding == ROUND) {\n        if (numerator % divisor > (divisor - 1) / 2) {\n            result++;\n        }\n    } else {\n        if (numerator % divisor > 0) {\n            result++;\n        }\n    }\n\n    return result;\n}\n\nlibrary FixLib {\n    /// Again, all arithmetic functions fail if and only if the result is out of bounds.\n\n    /// Convert this fixed-point value to a uint. Round towards zero if needed.\n    /// @return x\n    // as-ints: x / 1e18\n    function toUint(uint192 x) internal pure returns (uint136) {\n        return toUint(x, FLOOR);\n    }\n\n    /// Convert this uint192 to a uint\n    /// @return x\n    // as-ints: x / 1e18 with rounding\n    function toUint(uint192 x, RoundingMode rounding) internal pure returns (uint136) {\n        return uint136(_divrnd(uint256(x), FIX_SCALE, rounding));\n    }\n\n    /// Return the uint192 shifted to the left by `decimal` digits\n    /// (Similar to a bitshift but in base 10)\n    /// @return x * 10**decimals\n    // as-ints: x * 10**decimals\n    function shiftl(uint192 x, int8 decimals) internal pure returns (uint192) {\n        return shiftl(x, decimals, FLOOR);\n    }\n\n    /// Return the uint192 shifted to the left by `decimal` digits\n    /// (Similar to a bitshift but in base 10)\n    /// @return x * 10**decimals\n    // as-ints: x * 10**decimals\n    function shiftl(\n        uint192 x,\n        int8 decimals,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        // Handle overflow cases\n        if (x == 0) return 0;\n        if (decimals <= -59) return (rounding == CEIL ? 1 : 0); // 59, because 1e58 > 2**192\n        if (58 <= decimals) revert UIntOutOfBounds(); // 58, because x * 1e58 > 2 ** 192 if x != 0\n\n        uint256 coeff = uint256(10**abs(decimals));\n        return _safeWrap(decimals >= 0 ? x * coeff : _divrnd(x, coeff, rounding));\n    }\n\n    /// Add a uint192 to this uint192\n    /// @return x + y\n    // as-ints: x + y\n    function plus(uint192 x, uint192 y) internal pure returns (uint192) {\n        return x + y;\n    }\n\n    /// Add a uint to this uint192\n    /// @return x + y\n    // as-ints: x + y*1e18\n    function plusu(uint192 x, uint256 y) internal pure returns (uint192) {\n        return _safeWrap(x + y * FIX_SCALE);\n    }\n\n    /// Subtract a uint192 from this uint192\n    /// @return x - y\n    // as-ints: x - y\n    function minus(uint192 x, uint192 y) internal pure returns (uint192) {\n        return x - y;\n    }\n\n    /// Subtract a uint from this uint192\n    /// @return x - y\n    // as-ints: x - y*1e18\n    function minusu(uint192 x, uint256 y) internal pure returns (uint192) {\n        return _safeWrap(uint256(x) - uint256(y * FIX_SCALE));\n    }\n\n    /// Multiply this uint192 by a uint192\n    /// Round truncated values to the nearest available value. 5e-19 rounds away from zero.\n    /// @return x * y\n    // as-ints: x * y/1e18  [division using ROUND, not FLOOR]\n    function mul(uint192 x, uint192 y) internal pure returns (uint192) {\n        return mul(x, y, ROUND);\n    }\n\n    /// Multiply this uint192 by a uint192\n    /// @return x * y\n    // as-ints: x * y/1e18\n    function mul(\n        uint192 x,\n        uint192 y,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        return _safeWrap(_divrnd(uint256(x) * uint256(y), FIX_SCALE, rounding));\n    }\n\n    /// Multiply this uint192 by a uint\n    /// @return x * y\n    // as-ints: x * y\n    function mulu(uint192 x, uint256 y) internal pure returns (uint192) {\n        return _safeWrap(x * y);\n    }\n\n    /// Divide this uint192 by a uint192\n    /// @return x / y\n    // as-ints: x * 1e18 / y\n    function div(uint192 x, uint192 y) internal pure returns (uint192) {\n        return div(x, y, FLOOR);\n    }\n\n    /// Divide this uint192 by a uint192\n    /// @return x / y\n    // as-ints: x * 1e18 / y\n    function div(\n        uint192 x,\n        uint192 y,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        // Multiply-in FIX_SCALE before dividing by y to preserve precision.\n        return _safeWrap(_divrnd(uint256(x) * FIX_SCALE, y, rounding));\n    }\n\n    /// Divide this uint192 by a uint\n    /// @return x / y\n    // as-ints: x / y\n    function divu(uint192 x, uint256 y) internal pure returns (uint192) {\n        return divu(x, y, FLOOR);\n    }\n\n    /// Divide this uint192 by a uint\n    /// @return x / y\n    // as-ints: x / y\n    function divu(\n        uint192 x,\n        uint256 y,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        return _safeWrap(_divrnd(x, y, rounding));\n    }\n\n    uint64 constant FIX_HALF = uint64(FIX_SCALE) / 2;\n\n    /// Raise this uint192 to a nonnegative integer power. Requires that x_ <= FIX_ONE\n    /// Gas cost is O(lg(y)), precision is +- 1e-18.\n    /// @return x_ ** y\n    // as-ints: x_ ** y / 1e18**(y-1)    <- technically correct for y = 0. :D\n    function powu(uint192 x_, uint48 y) internal pure returns (uint192) {\n        require(x_ <= FIX_ONE);\n        if (y == 1) return x_;\n        if (x_ == FIX_ONE || y == 0) return FIX_ONE;\n        uint256 x = uint256(x_) * FIX_SCALE; // x is D36\n        uint256 result = FIX_SCALE_SQ; // result is D36\n        while (true) {\n            if (y & 1 == 1) result = (result * x + FIX_SCALE_SQ / 2) / FIX_SCALE_SQ;\n            if (y <= 1) break;\n            y = (y >> 1);\n            x = (x * x + FIX_SCALE_SQ / 2) / FIX_SCALE_SQ;\n        }\n        return _safeWrap(result / FIX_SCALE);\n    }\n\n    /// Comparison operators...\n    function lt(uint192 x, uint192 y) internal pure returns (bool) {\n        return x < y;\n    }\n\n    function lte(uint192 x, uint192 y) internal pure returns (bool) {\n        return x <= y;\n    }\n\n    function gt(uint192 x, uint192 y) internal pure returns (bool) {\n        return x > y;\n    }\n\n    function gte(uint192 x, uint192 y) internal pure returns (bool) {\n        return x >= y;\n    }\n\n    function eq(uint192 x, uint192 y) internal pure returns (bool) {\n        return x == y;\n    }\n\n    function neq(uint192 x, uint192 y) internal pure returns (bool) {\n        return x != y;\n    }\n\n    /// Return whether or not this uint192 is less than epsilon away from y.\n    /// @return |x - y| < epsilon\n    // as-ints: |x - y| < epsilon\n    function near(\n        uint192 x,\n        uint192 y,\n        uint192 epsilon\n    ) internal pure returns (bool) {\n        uint192 diff = x <= y ? y - x : x - y;\n        return diff < epsilon;\n    }\n\n    // ================ Chained Operations ================\n    // The operation foo_bar() always means:\n    //   Do foo() followed by bar(), and overflow only if the _end_ result doesn't fit in an uint192\n\n    /// Shift this uint192 left by `decimals` digits, and convert to a uint\n    /// @return x * 10**decimals\n    // as-ints: x * 10**(decimals - 18)\n    function shiftl_toUint(uint192 x, int8 decimals) internal pure returns (uint256) {\n        return shiftl_toUint(x, decimals, FLOOR);\n    }\n\n    /// Shift this uint192 left by `decimals` digits, and convert to a uint.\n    /// @return x * 10**decimals\n    // as-ints: x * 10**(decimals - 18)\n    function shiftl_toUint(\n        uint192 x,\n        int8 decimals,\n        RoundingMode rounding\n    ) internal pure returns (uint256) {\n        // Handle overflow cases\n        if (x == 0) return 0; // always computable, no matter what decimals is\n        if (decimals <= -42) return (rounding == CEIL ? 1 : 0);\n        if (96 <= decimals) revert UIntOutOfBounds();\n\n        decimals -= 18; // shift so that toUint happens at the same time.\n\n        uint256 coeff = uint256(10**abs(decimals));\n        return decimals >= 0 ? uint256(x * coeff) : uint256(_divrnd(x, coeff, rounding));\n    }\n\n    /// Multiply this uint192 by a uint, and output the result as a uint\n    /// @return x * y\n    // as-ints: x * y / 1e18\n    function mulu_toUint(uint192 x, uint256 y) internal pure returns (uint256) {\n        return mulDiv256(uint256(x), y, FIX_SCALE);\n    }\n\n    /// Multiply this uint192 by a uint, and output the result as a uint\n    /// @return x * y\n    // as-ints: x * y / 1e18\n    function mulu_toUint(\n        uint192 x,\n        uint256 y,\n        RoundingMode rounding\n    ) internal pure returns (uint256) {\n        return mulDiv256(uint256(x), y, FIX_SCALE, rounding);\n    }\n\n    /// Multiply this uint192 by a uint192 and output the result as a uint\n    /// @return x * y\n    // as-ints: x * y / 1e36\n    function mul_toUint(uint192 x, uint192 y) internal pure returns (uint256) {\n        return mulDiv256(uint256(x), uint256(y), FIX_SCALE_SQ);\n    }\n\n    /// Multiply this uint192 by a uint192 and output the result as a uint\n    /// @return x * y\n    // as-ints: x * y / 1e36\n    function mul_toUint(\n        uint192 x,\n        uint192 y,\n        RoundingMode rounding\n    ) internal pure returns (uint256) {\n        return mulDiv256(uint256(x), uint256(y), FIX_SCALE_SQ, rounding);\n    }\n\n    /// Compute x * y / z avoiding intermediate overflow\n    /// @dev Only use if you need to avoid overflow; costlier than x * y / z\n    /// @return x * y / z\n    // as-ints: x * y / z\n    function muluDivu(\n        uint192 x,\n        uint256 y,\n        uint256 z\n    ) internal pure returns (uint192) {\n        return muluDivu(x, y, z, FLOOR);\n    }\n\n    /// Compute x * y / z, avoiding intermediate overflow\n    /// @dev Only use if you need to avoid overflow; costlier than x * y / z\n    /// @return x * y / z\n    // as-ints: x * y / z\n    function muluDivu(\n        uint192 x,\n        uint256 y,\n        uint256 z,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        return _safeWrap(mulDiv256(x, y, z, rounding));\n    }\n\n    /// Compute x * y / z on Fixes, avoiding intermediate overflow\n    /// @dev Only use if you need to avoid overflow; costlier than x * y / z\n    /// @return x * y / z\n    // as-ints: x * y / z\n    function mulDiv(\n        uint192 x,\n        uint192 y,\n        uint192 z\n    ) internal pure returns (uint192) {\n        return mulDiv(x, y, z, FLOOR);\n    }\n\n    /// Compute x * y / z on Fixes, avoiding intermediate overflow\n    /// @dev Only use if you need to avoid overflow; costlier than x * y / z\n    /// @return x * y / z\n    // as-ints: x * y / z\n    function mulDiv(\n        uint192 x,\n        uint192 y,\n        uint192 z,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        return _safeWrap(mulDiv256(x, y, z, rounding));\n    }\n\n    // === safe*() ===\n\n    /// Multiply two fixes, rounding up to FIX_MAX and down to 0\n    /// @param a First param to multiply\n    /// @param b Second param to multiply\n    function safeMul(\n        uint192 a,\n        uint192 b,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        // untestable:\n        //      a will never = 0 here because of the check in _price()\n        if (a == 0 || b == 0) return 0;\n        // untestable:\n        //      a = FIX_MAX iff b = 0\n        if (a == FIX_MAX || b == FIX_MAX) return FIX_MAX;\n\n        // return FIX_MAX instead of throwing overflow errors.\n        unchecked {\n            // p and mul *are* Fix values, so have 18 decimals (D18)\n            uint256 rawDelta = uint256(b) * a; // {D36} = {D18} * {D18}\n            // if we overflowed, then return FIX_MAX\n            if (rawDelta / b != a) return FIX_MAX;\n            uint256 shiftDelta = rawDelta;\n\n            // add in rounding\n            if (rounding == RoundingMode.ROUND) shiftDelta += (FIX_ONE / 2);\n            else if (rounding == RoundingMode.CEIL) shiftDelta += FIX_ONE - 1;\n\n            // untestable (here there be dragons):\n            // (below explanation is for the ROUND case, but it extends to the FLOOR/CEIL too)\n            //          A)  shiftDelta = rawDelta + (FIX_ONE / 2)\n            //      shiftDelta overflows if:\n            //          B)  shiftDelta = MAX_UINT256 - FIX_ONE/2 + 1\n            //              rawDelta + (FIX_ONE/2) = MAX_UINT256 - FIX_ONE/2 + 1\n            //              b * a = MAX_UINT256 - FIX_ONE + 1\n            //      therefore shiftDelta overflows if:\n            //          C)  b = (MAX_UINT256 - FIX_ONE + 1) / a\n            //      MAX_UINT256 ~= 1e77 , FIX_MAX ~= 6e57 (6e20 difference in magnitude)\n            //      a <= 1e21 (MAX_TARGET_AMT)\n            //      a must be between 1e19 & 1e20 in order for b in (C) to be uint192,\n            //      but a would have to be < 1e18 in order for (A) to overflow\n            if (shiftDelta < rawDelta) return FIX_MAX;\n\n            // return FIX_MAX if return result would truncate\n            if (shiftDelta / FIX_ONE > FIX_MAX) return FIX_MAX;\n\n            // return _div(rawDelta, FIX_ONE, rounding)\n            return uint192(shiftDelta / FIX_ONE); // {D18} = {D36} / {D18}\n        }\n    }\n\n    /// Divide two fixes, rounding up to FIX_MAX and down to 0\n    /// @param a Numerator\n    /// @param b Denominator\n    function safeDiv(\n        uint192 a,\n        uint192 b,\n        RoundingMode rounding\n    ) internal pure returns (uint192) {\n        if (a == 0) return 0;\n        if (b == 0) return FIX_MAX;\n\n        uint256 raw = _divrnd(FIX_ONE_256 * a, uint256(b), rounding);\n        if (raw >= FIX_MAX) return FIX_MAX;\n        return uint192(raw); // don't need _safeWrap\n    }\n\n    /// Multiplies two fixes and divide by a third\n    /// @param a First to multiply\n    /// @param b Second to multiply\n    /// @param c Denominator\n    function safeMulDiv(\n        uint192 a,\n        uint192 b,\n        uint192 c,\n        RoundingMode rounding\n    ) internal pure returns (uint192 result) {\n        if (a == 0 || b == 0) return 0;\n        if (a == FIX_MAX || b == FIX_MAX || c == 0) return FIX_MAX;\n\n        uint256 result_256;\n        unchecked {\n            (uint256 hi, uint256 lo) = fullMul(a, b);\n            if (hi >= c) return FIX_MAX;\n            uint256 mm = mulmod(a, b, c);\n            if (mm > lo) hi -= 1;\n            lo -= mm;\n            uint256 pow2 = c & (0 - c);\n\n            uint256 c_256 = uint256(c);\n            // Warning: Should not access c below this line\n\n            c_256 /= pow2;\n            lo /= pow2;\n            lo += hi * ((0 - pow2) / pow2 + 1);\n            uint256 r = 1;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            r *= 2 - c_256 * r;\n            result_256 = lo * r;\n\n            // Apply rounding\n            if (rounding == CEIL) {\n                if (mm > 0) result_256 += 1;\n            } else if (rounding == ROUND) {\n                if (mm > ((c_256 - 1) / 2)) result_256 += 1;\n            }\n        }\n\n        if (result_256 >= FIX_MAX) return FIX_MAX;\n        return uint192(result_256);\n    }\n}\n\n// ================ a couple pure-uint helpers================\n// as-ints comments are omitted here, because they're the same as @return statements, because\n// these are all pure uint functions\n\n/// Return (x*y/z), avoiding intermediate overflow.\n//  Adapted from sources:\n//    https://medium.com/coinmonks/4db014e080b1, https://medium.com/wicketh/afa55870a65\n//    and quite a few of the other excellent \"Mathemagic\" posts from https://medium.com/wicketh\n/// @dev Only use if you need to avoid overflow; costlier than x * y / z\n/// @return result x * y / z\nfunction mulDiv256(\n    uint256 x,\n    uint256 y,\n    uint256 z\n) pure returns (uint256 result) {\n    unchecked {\n        (uint256 hi, uint256 lo) = fullMul(x, y);\n        if (hi >= z) revert UIntOutOfBounds();\n        uint256 mm = mulmod(x, y, z);\n        if (mm > lo) hi -= 1;\n        lo -= mm;\n        uint256 pow2 = z & (0 - z);\n        z /= pow2;\n        lo /= pow2;\n        lo += hi * ((0 - pow2) / pow2 + 1);\n        uint256 r = 1;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        r *= 2 - z * r;\n        result = lo * r;\n    }\n}\n\n/// Return (x*y/z), avoiding intermediate overflow.\n/// @dev Only use if you need to avoid overflow; costlier than x * y / z\n/// @return x * y / z\nfunction mulDiv256(\n    uint256 x,\n    uint256 y,\n    uint256 z,\n    RoundingMode rounding\n) pure returns (uint256) {\n    uint256 result = mulDiv256(x, y, z);\n    if (rounding == FLOOR) return result;\n\n    uint256 mm = mulmod(x, y, z);\n    if (rounding == CEIL) {\n        if (mm > 0) result += 1;\n    } else {\n        if (mm > ((z - 1) / 2)) result += 1; // z should be z-1\n    }\n    return result;\n}\n\n/// Return (x*y) as a \"virtual uint512\" (lo, hi), representing (hi*2**256 + lo)\n///   Adapted from sources:\n///   https://medium.com/wicketh/27650fec525d, https://medium.com/coinmonks/4db014e080b1\n/// @dev Intended to be internal to this library\n/// @return hi (hi, lo) satisfies  hi*(2**256) + lo == x * y\n/// @return lo (paired with `hi`)\nfunction fullMul(uint256 x, uint256 y) pure returns (uint256 hi, uint256 lo) {\n    unchecked {\n        uint256 mm = mulmod(x, y, uint256(0) - uint256(1));\n        lo = x * y;\n        hi = mm - lo;\n        if (mm < lo) hi -= 1;\n    }\n}\n"
      },
      "contracts/libraries/Throttle.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"./Fixed.sol\";\n\nuint48 constant ONE_HOUR = 3600; // {seconds/hour}\n\n/**\n * @title ThrottleLib\n * A library that implements a usage throttle that can be used to ensure net issuance\n * or net redemption for an RToken never exceeds some bounds per unit time (hour).\n *\n * It is expected for the RToken to use this library with two instances, one for issuance\n * and one for redemption. Issuance causes the available redemption amount to increase, and\n * visa versa.\n */\nlibrary ThrottleLib {\n    using FixLib for uint192;\n\n    struct Params {\n        uint256 amtRate; // {qRTok/hour} a quantity of RToken hourly; cannot be 0\n        uint192 pctRate; // {1/hour} a fraction of RToken hourly; can be 0\n    }\n\n    struct Throttle {\n        // === Gov params ===\n        Params params;\n        // === Cache ===\n        uint48 lastTimestamp; // {seconds}\n        uint256 lastAvailable; // {qRTok}\n    }\n\n    /// Reverts if usage amount exceeds available amount\n    /// @param supply {qRTok} Total RToken supply beforehand\n    /// @param amount {qRTok} Amount of RToken to use. Should be negative for the issuance\n    ///   throttle during redemption and for the redemption throttle during issuance.\n    function useAvailable(\n        Throttle storage throttle,\n        uint256 supply,\n        int256 amount\n    ) internal {\n        // untestable: amtRate will always be greater > 0 due to previous validations\n        if (throttle.params.amtRate == 0 && throttle.params.pctRate == 0) return;\n\n        // Calculate hourly limit\n        uint256 limit = hourlyLimit(throttle, supply); // {qRTok}\n\n        // Calculate available amount before supply change\n        uint256 available = currentlyAvailable(throttle, limit);\n\n        // Update throttle.timestamp if available amount changed or at limit\n        if (available != throttle.lastAvailable || available == limit) {\n            throttle.lastTimestamp = uint48(block.timestamp);\n        }\n\n        // Update throttle.lastAvailable\n        if (amount > 0) {\n            require(uint256(amount) <= available, \"supply change throttled\");\n            available -= uint256(amount);\n            // untestable: the final else statement, amount will never be 0\n        } else if (amount < 0) {\n            available += uint256(-amount);\n        }\n        throttle.lastAvailable = available;\n    }\n\n    /// @param limit {qRTok/hour} The hourly limit\n    /// @return available {qRTok} Amount currently available for consumption\n    function currentlyAvailable(Throttle storage throttle, uint256 limit)\n        internal\n        view\n        returns (uint256 available)\n    {\n        uint48 delta = uint48(block.timestamp) - throttle.lastTimestamp; // {seconds}\n        available = throttle.lastAvailable + (limit * delta) / ONE_HOUR;\n        if (available > limit) available = limit;\n    }\n\n    /// @return limit {qRTok} The hourly limit\n    function hourlyLimit(Throttle storage throttle, uint256 supply)\n        internal\n        view\n        returns (uint256 limit)\n    {\n        Params storage params = throttle.params;\n\n        // Calculate hourly limit as: max(params.amtRate, supply.mul(params.pctRate))\n        limit = (supply * params.pctRate) / FIX_ONE_256; // {qRTok}\n        if (params.amtRate > limit) limit = params.amtRate;\n    }\n}\n"
      },
      "contracts/p1/mixins/RecollateralizationLib.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../../interfaces/IAsset.sol\";\nimport \"../../interfaces/IAssetRegistry.sol\";\nimport \"../../interfaces/IBackingManager.sol\";\nimport \"../../libraries/Fixed.sol\";\nimport \"./TradeLib.sol\";\n\n/// Struct purposes:\n///   1. Configure trading\n///   2. Stay under stack limit with fewer vars\n///   3. Cache information such as component addresses to save on gas\nstruct TradingContext {\n    BasketRange basketsHeld; // {BU}\n    // basketsHeld.top is the number of partial baskets units held\n    // basketsHeld.bottom is the number of full basket units held\n\n    // Components\n    IBackingManager bm;\n    IBasketHandler bh;\n    IAssetRegistry ar;\n    IStRSR stRSR;\n    IERC20 rsr;\n    IRToken rToken;\n    // Gov Vars\n    uint192 minTradeVolume; // {UoA}\n    uint192 maxTradeSlippage; // {1}\n    // Cached values\n    uint192[] quantities; // {tok/BU} basket quantities\n}\n\n/**\n * @title RecollateralizationLibP1\n * @notice An informal extension of BackingManager that implements the rebalancing logic\n *   Users:\n *     - BackingManager\n *     - RTokenAsset (uses `basketRange()`)\n *\n * Interface:\n *  1. prepareRecollateralizationTrade() (external)\n *  2. basketRange() (internal)\n */\nlibrary RecollateralizationLibP1 {\n    using FixLib for uint192;\n    using TradeLib for TradeInfo;\n    using TradeLib for IBackingManager;\n\n    /// Select and prepare a trade that moves us closer to capitalization, using the\n    /// basket range to avoid overeager/duplicate trading.\n    /// The basket range is the full range of projected outcomes for the rebalancing process.\n    // This is the \"main loop\" for recollateralization trading:\n    // actions:\n    //   let range = basketRange(...)\n    //   let trade = nextTradePair(...)\n    //   if trade.sell is not a defaulted collateral, prepareTradeToCoverDeficit(...)\n    //   otherwise, prepareTradeSell(...) taking the minBuyAmount as the dependent variable\n    function prepareRecollateralizationTrade(IBackingManager bm, BasketRange memory basketsHeld)\n        external\n        view\n        returns (\n            bool doTrade,\n            TradeRequest memory req,\n            TradePrices memory prices\n        )\n    {\n        IMain main = bm.main();\n\n        // === Prepare TradingContext cache ===\n        TradingContext memory ctx;\n\n        ctx.basketsHeld = basketsHeld;\n        ctx.bm = bm;\n        ctx.bh = main.basketHandler();\n        ctx.ar = main.assetRegistry();\n        ctx.stRSR = main.stRSR();\n        ctx.rsr = main.rsr();\n        ctx.rToken = main.rToken();\n        ctx.minTradeVolume = bm.minTradeVolume();\n        ctx.maxTradeSlippage = bm.maxTradeSlippage();\n\n        // Calculate quantities\n        Registry memory reg = ctx.ar.getRegistry();\n        ctx.quantities = new uint192[](reg.erc20s.length);\n        for (uint256 i = 0; i < reg.erc20s.length; ++i) {\n            ctx.quantities[i] = ctx.bh.quantityUnsafe(reg.erc20s[i], reg.assets[i]);\n        }\n\n        // ============================\n\n        // Compute a target basket range for trading -  {BU}\n        BasketRange memory range = basketRange(ctx, reg);\n\n        // Select a pair to trade next, if one exists\n        TradeInfo memory trade = nextTradePair(ctx, reg, range);\n\n        // Don't trade if no pair is selected\n        if (address(trade.sell) == address(0) || address(trade.buy) == address(0)) {\n            return (false, req, prices);\n        }\n\n        // If we are selling a fully unpriced asset or UNSOUND collateral, do not cover deficit\n        // untestable:\n        //     sellLow will not be zero, those assets are skipped in nextTradePair\n        if (\n            trade.prices.sellLow == 0 ||\n            (trade.sell.isCollateral() &&\n                ICollateral(address(trade.sell)).status() != CollateralStatus.SOUND)\n        ) {\n            // Emergency case\n            // Set minBuyAmount as a function of sellAmount\n            (doTrade, req) = trade.prepareTradeSell(ctx.minTradeVolume, ctx.maxTradeSlippage);\n        } else {\n            // Normal case\n            // Set sellAmount as a function of minBuyAmount\n            (doTrade, req) = trade.prepareTradeToCoverDeficit(\n                ctx.minTradeVolume,\n                ctx.maxTradeSlippage\n            );\n        }\n\n        // At this point doTrade _must_ be true, otherwise nextTradePair assumptions are broken\n        assert(doTrade);\n\n        return (doTrade, req, trade.prices);\n    }\n\n    // Compute the target basket range\n    // Algorithm intuition: Trade conservatively. Quantify uncertainty based on the proportion of\n    // token balances requiring trading vs not requiring trading. Seek to decrease uncertainty\n    // the largest amount possible with each trade.\n    //\n    // How do we know this algorithm converges?\n    // Assumption: constant oracle prices; monotonically increasing refPerTok()\n    // Any volume traded narrows the BU band. Why:\n    //   - We might increase `basketsHeld.bottom` from run-to-run, but will never decrease it\n    //   - We might decrease the UoA amount of excess balances beyond `basketsHeld.bottom` from\n    //       run-to-run, but will never increase it\n    //   - We might decrease the UoA amount of missing balances up-to `basketsHeld.top` from\n    //       run-to-run, but will never increase it\n    //\n    // Preconditions:\n    // - ctx is correctly populated, with current basketsHeld.bottom + basketsHeld.top\n    // - reg contains erc20 + asset + quantities arrays in same order and without duplicates\n    // Trading Strategy:\n    // - We will not aim to hold more than rToken.basketsNeeded() BUs\n    // - No double trades: if we buy B in one trade, we won't sell B in another trade\n    //       Caveat: Unless the asset we're selling is IFFY/DISABLED\n    // - The best price we might get for a trade is at the high sell price and low buy price\n    // - The worst price we might get for a trade is at the low sell price and\n    //     the high buy price, multiplied by ( 1 - maxTradeSlippage )\n    // - In the worst-case an additional dust balance can be lost, up to minTradeVolume\n    // - Given all that, we're aiming to hold as many BUs as possible using the assets we own.\n    //\n    // More concretely:\n    // - range.top = min(rToken.basketsNeeded, basketsHeld.top - least baskets missing\n    //                                                                   + most baskets surplus)\n    // - range.bottom = min(rToken.basketsNeeded, basketsHeld.bottom + least baskets purchaseable)\n    //   where \"least baskets purchaseable\" involves trading at the worst price,\n    //   incurring the full maxTradeSlippage, and taking up to a minTradeVolume loss due to dust.\n    function basketRange(TradingContext memory ctx, Registry memory reg)\n        internal\n        view\n        returns (BasketRange memory range)\n    {\n        (uint192 buPriceLow, uint192 buPriceHigh) = ctx.bh.lotPrice(); // {UoA/BU}\n        uint192 basketsNeeded = ctx.rToken.basketsNeeded(); // {BU}\n\n        // Cap ctx.basketsHeld.top\n        if (ctx.basketsHeld.top > basketsNeeded) {\n            ctx.basketsHeld.top = basketsNeeded;\n        }\n\n        // === (1/3) Calculate contributions from surplus/deficits ===\n\n        // for range.top, anchor to min(ctx.basketsHeld.top, basketsNeeded)\n        // for range.bottom, anchor to min(ctx.basketsHeld.bottom, basketsNeeded)\n\n        // a signed delta to be applied to range.top\n        int256 deltaTop; // D18{BU} even though this is int256, it is D18\n        // not required for range.bottom\n\n        // to minimize total operations, range.bottom is calculated from a summed UoA\n        uint192 uoaBottom; // {UoA} pessimistic UoA estimate of balances above basketsHeld.bottom\n\n        // (no space on the stack to cache erc20s.length)\n        for (uint256 i = 0; i < reg.erc20s.length; ++i) {\n            // Exclude RToken balances to avoid double counting value\n            if (reg.erc20s[i] == IERC20(address(ctx.rToken))) continue;\n\n            uint192 bal = reg.assets[i].bal(address(ctx.bm)); // {tok}\n\n            // For RSR, include the staking balance\n            if (reg.erc20s[i] == ctx.rsr) {\n                bal = bal.plus(reg.assets[i].bal(address(ctx.stRSR)));\n            }\n\n            if (ctx.quantities[i] == 0) {\n                // Skip over dust-balance assets not in the basket\n                (uint192 lotLow, ) = reg.assets[i].lotPrice(); // {UoA/tok}\n\n                // Intentionally include value of IFFY/DISABLED collateral\n                if (!TradeLib.isEnoughToSell(reg.assets[i], bal, lotLow, ctx.minTradeVolume)) {\n                    continue;\n                }\n            }\n\n            (uint192 low, uint192 high) = reg.assets[i].price(); // {UoA/tok}\n            // price() is better than lotPrice() here: it's important to not underestimate how\n            // much value could be in a token that is unpriced by using a decaying high lotPrice.\n            // price() will return [0, FIX_MAX] in this case, which is preferable.\n\n            // throughout these sections +/- is same as Fix.plus/Fix.minus and </> is Fix.gt/.lt\n\n            // deltaTop: optimistic case\n            // if in deficit relative to ctx.basketsHeld.top: deduct missing baskets\n            // if in surplus relative to ctx.basketsHeld.top: add-in surplus baskets\n            {\n                // {tok} = {tok/BU} * {BU}\n                uint192 anchor = ctx.quantities[i].mul(ctx.basketsHeld.top, CEIL);\n\n                if (anchor > bal) {\n                    // deficit: deduct optimistic estimate of baskets missing\n\n                    // {BU} = {UoA/tok} * {tok} / {UoA/BU}\n                    deltaTop -= int256(uint256(low.mulDiv(anchor - bal, buPriceHigh, FLOOR)));\n                    // does not need underflow protection: using low price of asset\n                } else {\n                    // surplus: add-in optimistic estimate of baskets purchaseable\n\n                    //  {BU} = {UoA/tok} * {tok} / {UoA/BU}\n                    deltaTop += int256(uint256(high.safeMulDiv(bal - anchor, buPriceLow, CEIL)));\n                }\n            }\n\n            // range.bottom: pessimistic case\n            // add-in surplus baskets relative to ctx.basketsHeld.bottom\n            {\n                // {tok} = {tok/BU} * {BU}\n                uint192 anchor = ctx.quantities[i].mul(ctx.basketsHeld.bottom, FLOOR);\n\n                // (1) Sum token value at low price\n                // {UoA} = {UoA/tok} * {tok}\n                uint192 val = low.mul(bal - anchor, FLOOR);\n\n                // (2) Lose minTradeVolume to dust (why: auctions can return tokens)\n                // Q: Why is this precisely where we should take out minTradeVolume?\n                // A: Our use of isEnoughToSell always uses the low price (lotLow, technically),\n                //   so min trade volumes are always assesed based on low prices. At this point\n                //   in the calculation we have already calculated the UoA amount corresponding to\n                //   the excess token balance based on its low price, so we are already set up\n                //   to straightforwardly deduct the minTradeVolume before trying to buy BUs.\n                uoaBottom += (val < ctx.minTradeVolume) ? 0 : val - ctx.minTradeVolume;\n            }\n        }\n\n        // ==== (2/3) Add-in ctx.*BasketsHeld safely ====\n\n        // range.top\n        if (deltaTop < 0) {\n            range.top = ctx.basketsHeld.top - _safeWrap(uint256(-deltaTop));\n            // reverting on underflow is appropriate here\n        } else {\n            // guard against overflow; > is same as Fix.gt\n            if (uint256(deltaTop) + ctx.basketsHeld.top > FIX_MAX) range.top = FIX_MAX;\n            else range.top = ctx.basketsHeld.top + _safeWrap(uint256(deltaTop));\n        }\n\n        // range.bottom\n        // (3) Buy BUs at their high price with the remaining value\n        // (4) Assume maximum slippage in trade\n        // {BU} = {UoA} * {1} / {UoA/BU}\n        range.bottom =\n            ctx.basketsHeld.bottom +\n            uoaBottom.mulDiv(FIX_ONE.minus(ctx.maxTradeSlippage), buPriceHigh, FLOOR);\n        // reverting on overflow is appropriate here\n\n        // ==== (3/3) Enforce (range.bottom <= range.top <= basketsNeeded) ====\n\n        if (range.top > basketsNeeded) range.top = basketsNeeded;\n        if (range.bottom > range.top) range.bottom = range.top;\n    }\n\n    // ===========================================================================================\n\n    // === Private ===\n\n    // Used in memory in `nextTradePair` to duck the stack limit\n    struct MaxSurplusDeficit {\n        CollateralStatus surplusStatus; // starts SOUND\n        uint192 surplus; // {UoA}\n        uint192 deficit; // {UoA}\n    }\n\n    // Choose next sell/buy pair to trade, with reference to the basket range\n    // Skip over trading surplus dust amounts\n    /// @return trade\n    ///   sell: Surplus collateral OR address(0)\n    ///   deficit Deficit collateral OR address(0)\n    ///   sellAmount {sellTok} Surplus amount (whole tokens)\n    ///   buyAmount {buyTok} Deficit amount (whole tokens)\n    ///   prices.sellLow {UoA/sellTok} The worst-case price of the sell token on secondary markets\n    ///   prices.sellHigh {UoA/sellTok} The best-case price of the sell token on secondary markets\n    ///   prices.buyLow {UoA/buyTok} The best-case price of the buy token on secondary markets\n    ///   prices.buyHigh {UoA/buyTok} The worst-case price of the buy token on secondary markets\n    ///\n    // Defining \"sell\" and \"buy\":\n    // If bal(e) > (quantity(e) * range.top), then e is in surplus by the difference\n    // If bal(e) < (quantity(e) * range.bottom), then e is in deficit by the difference\n    //\n    // First, ignoring RSR:\n    //   `trade.sell` is the token from erc20s with the greatest surplus value (in UoA),\n    //   and sellAmount is the quantity of that token that it's in surplus (in qTok).\n    //   if `trade.sell` == 0, then no token is in surplus by at least minTradeSize,\n    //        and `trade.sellAmount` and `trade.sellLow` / `trade.sellHigh are unset.\n    //\n    //   `trade.buy` is the token from erc20s with the greatest deficit value (in UoA),\n    //   and buyAmount is the quantity of that token that it's in deficit (in qTok).\n    //   if `trade.buy` == 0, then no token is in deficit at all,\n    //        and `trade.buyAmount` and `trade.buyLow` / `trade.buyHigh` are unset.\n    //\n    // Then, just if we have a buy asset and no sell asset, consider selling available RSR.\n    //\n    // Prefer selling assets in this order: DISABLED -> SOUND -> IFFY.\n    // Sell IFFY last because it may recover value in the future.\n    // All collateral in the basket have already been guaranteed to be SOUND by upstream checks.\n    function nextTradePair(\n        TradingContext memory ctx,\n        Registry memory reg,\n        BasketRange memory range\n    ) private view returns (TradeInfo memory trade) {\n        MaxSurplusDeficit memory maxes;\n        maxes.surplusStatus = CollateralStatus.IFFY; // least-desirable sell status\n\n        // Iterate over non-RSR/non-RToken assets\n        // (no space on the stack to cache erc20s.length)\n        for (uint256 i = 0; i < reg.erc20s.length; ++i) {\n            if (reg.erc20s[i] == ctx.rsr || address(reg.erc20s[i]) == address(ctx.rToken)) continue;\n\n            uint192 bal = reg.assets[i].bal(address(ctx.bm)); // {tok}\n\n            // {tok} = {BU} * {tok/BU}\n            // needed(Top): token balance needed for range.top baskets: quantity(e) * range.top\n            uint192 needed = range.top.mul(ctx.quantities[i], CEIL); // {tok}\n\n            if (bal.gt(needed)) {\n                (uint192 lotLow, uint192 lotHigh) = reg.assets[i].lotPrice(); // {UoA/sellTok}\n                if (lotHigh == 0) continue; // skip over worthless assets\n\n                // {UoA} = {sellTok} * {UoA/sellTok}\n                uint192 delta = bal.minus(needed).mul(lotLow, FLOOR);\n\n                // status = asset.status() if asset.isCollateral() else SOUND\n                CollateralStatus status; // starts SOUND\n                if (reg.assets[i].isCollateral()) {\n                    status = ICollateral(address(reg.assets[i])).status();\n                }\n\n                // Select the most-in-surplus \"best\" asset still enough to sell,\n                // as defined by a (status, surplusAmt) ordering\n                if (\n                    isBetterSurplus(maxes, status, delta) &&\n                    TradeLib.isEnoughToSell(\n                        reg.assets[i],\n                        bal.minus(needed),\n                        lotLow,\n                        ctx.minTradeVolume\n                    )\n                ) {\n                    trade.sell = reg.assets[i];\n                    trade.sellAmount = bal.minus(needed);\n                    trade.prices.sellLow = lotLow;\n                    trade.prices.sellHigh = lotHigh;\n\n                    maxes.surplusStatus = status;\n                    maxes.surplus = delta;\n                }\n            } else {\n                // needed(Bottom): token balance needed at bottom of the basket range\n                needed = range.bottom.mul(ctx.quantities[i], CEIL); // {buyTok};\n\n                if (bal.lt(needed)) {\n                    uint192 amtShort = needed.minus(bal); // {buyTok}\n                    (uint192 lotLow, uint192 lotHigh) = reg.assets[i].lotPrice(); // {UoA/buyTok}\n\n                    // {UoA} = {buyTok} * {UoA/buyTok}\n                    uint192 delta = amtShort.mul(lotHigh, CEIL);\n\n                    // The best asset to buy is whichever asset has the largest deficit\n                    if (delta.gt(maxes.deficit)) {\n                        trade.buy = reg.assets[i];\n                        trade.buyAmount = amtShort;\n                        trade.prices.buyLow = lotLow;\n                        trade.prices.buyHigh = lotHigh;\n\n                        maxes.deficit = delta;\n                    }\n                }\n            }\n        }\n\n        // Use RSR if needed\n        if (address(trade.sell) == address(0) && address(trade.buy) != address(0)) {\n            IAsset rsrAsset = ctx.ar.toAsset(ctx.rsr);\n\n            uint192 rsrAvailable = rsrAsset.bal(address(ctx.bm)).plus(\n                rsrAsset.bal(address(ctx.stRSR))\n            );\n            (uint192 lotLow, uint192 lotHigh) = rsrAsset.lotPrice(); // {UoA/RSR}\n\n            if (\n                lotHigh > 0 &&\n                TradeLib.isEnoughToSell(rsrAsset, rsrAvailable, lotLow, ctx.minTradeVolume)\n            ) {\n                trade.sell = rsrAsset;\n                trade.sellAmount = rsrAvailable;\n                trade.prices.sellLow = lotLow;\n                trade.prices.sellHigh = lotHigh;\n            }\n        }\n    }\n\n    /// @param curr The current MaxSurplusDeficit containing the best surplus so far\n    /// @param other The collateral status of the asset in consideration\n    /// @param surplusAmt {UoA} The amount by which the asset in consideration is in surplus\n    function isBetterSurplus(\n        MaxSurplusDeficit memory curr,\n        CollateralStatus other,\n        uint192 surplusAmt\n    ) private pure returns (bool) {\n        // NOTE: If the CollateralStatus enum changes then this has to change!\n        if (curr.surplusStatus == CollateralStatus.DISABLED) {\n            return other == CollateralStatus.DISABLED && surplusAmt.gt(curr.surplus);\n        } else if (curr.surplusStatus == CollateralStatus.SOUND) {\n            return\n                other == CollateralStatus.DISABLED ||\n                (other == CollateralStatus.SOUND && surplusAmt.gt(curr.surplus));\n        } else {\n            // curr is IFFY\n            return other != CollateralStatus.IFFY || surplusAmt.gt(curr.surplus);\n        }\n    }\n}\n"
      },
      "contracts/p1/mixins/TradeLib.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../../interfaces/IAsset.sol\";\nimport \"../../interfaces/IAssetRegistry.sol\";\nimport \"../../interfaces/ITrading.sol\";\nimport \"../../libraries/Fixed.sol\";\nimport \"./RecollateralizationLib.sol\";\n\nstruct TradeInfo {\n    IAsset sell;\n    IAsset buy;\n    uint192 sellAmount; // {sellTok}\n    uint192 buyAmount; // {buyTok}\n    TradePrices prices;\n}\n\n/**\n * @title TradeLib\n * @notice An internal lib for preparing individual trades on particular asset pairs\n *   Users:\n *     - RecollateralizationLib\n *     - RevenueTrader\n */\nlibrary TradeLib {\n    using FixLib for uint192;\n\n    /// Prepare a trade to sell `trade.sellAmount` that guarantees a reasonable closing price,\n    /// without explicitly aiming at a particular buy amount.\n    /// @param trade:\n    ///   sell != 0, sellAmount >= 0 {sellTok}, prices.sellLow >= 0 {UoA/sellTok}\n    ///   buy != 0, buyAmount (unused) {buyTok}, prices.buyHigh > 0 {UoA/buyTok}\n    /// @return notDust True when the trade is larger than the dust amount\n    /// @return req The prepared trade request to send to the Broker\n    //\n    // If notDust is true, then the returned trade request satisfies:\n    //   req.sell == trade.sell and req.buy == trade.buy,\n    //   req.minBuyAmount * trade.prices.buyHigh ~=\n    //        trade.sellAmount * trade.prices.sellLow * (1-maxTradeSlippage),\n    //   req.sellAmount == min(trade.sell.maxTradeSize(), trade.sellAmount)\n    //   1 < req.sellAmount\n    //\n    // If notDust is false, no trade exists that satisfies those constraints.\n    function prepareTradeSell(\n        TradeInfo memory trade,\n        uint192 minTradeVolume,\n        uint192 maxTradeSlippage\n    ) internal view returns (bool notDust, TradeRequest memory req) {\n        // checked for in RevenueTrader / CollateralizatlionLib\n        assert(\n            trade.prices.buyHigh > 0 &&\n                trade.prices.buyHigh < FIX_MAX &&\n                trade.prices.sellLow < FIX_MAX\n        );\n\n        notDust = isEnoughToSell(\n            trade.sell,\n            trade.sellAmount,\n            trade.prices.sellLow,\n            minTradeVolume\n        );\n\n        // Cap sell amount\n        uint192 maxSell = maxTradeSize(trade.sell, trade.buy, trade.prices.sellHigh); // {sellTok}\n        uint192 s = trade.sellAmount > maxSell ? maxSell : trade.sellAmount; // {sellTok}\n\n        // Calculate equivalent buyAmount within [0, FIX_MAX]\n        // {buyTok} = {sellTok} * {1} * {UoA/sellTok} / {UoA/buyTok}\n        uint192 b = s.mul(FIX_ONE.minus(maxTradeSlippage)).safeMulDiv(\n            trade.prices.sellLow,\n            trade.prices.buyHigh,\n            CEIL\n        );\n\n        // {*tok} => {q*Tok}\n        req.sellAmount = s.shiftl_toUint(int8(trade.sell.erc20Decimals()), FLOOR);\n        req.minBuyAmount = b.shiftl_toUint(int8(trade.buy.erc20Decimals()), CEIL);\n        req.sell = trade.sell;\n        req.buy = trade.buy;\n\n        return (notDust, req);\n    }\n\n    /// Assuming we have `trade.sellAmount` sell tokens available, prepare a trade to cover as\n    /// much of our deficit of `trade.buyAmount` buy tokens as possible, given expected trade\n    /// slippage and maxTradeVolume().\n    /// @param trade:\n    ///   sell != 0\n    ///   buy != 0\n    ///   sellAmount (unused) {sellTok}\n    ///   buyAmount >= 0 {buyTok}\n    ///   prices.sellLow > 0 {UoA/sellTok}\n    ///   prices.buyHigh > 0 {UoA/buyTok}\n    /// @return notDust Whether the prepared trade is large enough to be worth trading\n    /// @return req The prepared trade request to send to the Broker\n    //\n    // Returns prepareTradeSell(trade, rules), where\n    //   req.sellAmount = min(trade.sellAmount,\n    //                trade.buyAmount * (buyHigh / sellLow) / (1-maxTradeSlippage))\n    //   i.e, the minimum of trade.sellAmount and (a sale amount that, at current prices and\n    //   maximum slippage, will yield at least the requested trade.buyAmount)\n    //\n    // Which means we should get that, if notDust is true, then:\n    //   req.sell = sell and req.buy = buy\n    //\n    //   1 <= req.minBuyAmount <= max(trade.buyAmount, buy.minTradeSize()))\n    //   1 < req.sellAmount <= min(trade.sellAmount, sell.maxTradeSize())\n    //   req.minBuyAmount ~= trade.sellAmount * sellLow / buyHigh * (1-maxTradeSlippage)\n    //\n    //   req.sellAmount (and req.minBuyAmount) are maximal satisfying all these conditions\n    function prepareTradeToCoverDeficit(\n        TradeInfo memory trade,\n        uint192 minTradeVolume,\n        uint192 maxTradeSlippage\n    ) internal view returns (bool notDust, TradeRequest memory req) {\n        assert(\n            trade.prices.sellLow > 0 &&\n                trade.prices.sellLow < FIX_MAX &&\n                trade.prices.buyHigh > 0 &&\n                trade.prices.buyHigh < FIX_MAX\n        );\n\n        // Don't buy dust.\n        trade.buyAmount = fixMax(\n            trade.buyAmount,\n            minTradeSize(minTradeVolume, trade.prices.buyHigh)\n        );\n\n        // {sellTok} = {buyTok} * {UoA/buyTok} / {UoA/sellTok}\n        uint192 exactSellAmount = trade.buyAmount.mulDiv(\n            trade.prices.buyHigh,\n            trade.prices.sellLow,\n            CEIL\n        );\n        // exactSellAmount: Amount to sell to buy `deficitAmount` if there's no slippage\n\n        // slippedSellAmount: Amount needed to sell to buy `deficitAmount`, counting slippage\n        uint192 slippedSellAmount = exactSellAmount.div(FIX_ONE.minus(maxTradeSlippage), CEIL);\n\n        trade.sellAmount = fixMin(slippedSellAmount, trade.sellAmount); // {sellTok}\n        return prepareTradeSell(trade, minTradeVolume, maxTradeSlippage);\n    }\n\n    /// @param asset The asset in consideration\n    /// @param amt {tok} The number of whole tokens we plan to sell\n    /// @param price {UoA/tok} The price to use for sizing\n    /// @param minTradeVolume {UoA} The min trade volume, passed in for gas optimization\n    /// @return If amt is sufficiently large to be worth selling into our trading platforms\n    function isEnoughToSell(\n        IAsset asset,\n        uint192 amt,\n        uint192 price,\n        uint192 minTradeVolume\n    ) internal view returns (bool) {\n        return\n            amt.gte(minTradeSize(minTradeVolume, price)) &&\n            // Trading platforms often don't allow token quanta trades for rounding reasons\n            // {qTok} = {tok} / {tok/qTok}\n            amt.shiftl_toUint(int8(asset.erc20Decimals())) > 1;\n    }\n\n    // === Private ===\n\n    /// Calculates the minTradeSize for an asset based on the given minTradeVolume and price\n    /// @param minTradeVolume {UoA} The min trade volume, passed in for gas optimization\n    /// @return {tok} The min trade size for the asset in whole tokens\n    function minTradeSize(uint192 minTradeVolume, uint192 price) private pure returns (uint192) {\n        // {tok} = {UoA} / {UoA/tok}\n        uint192 size = price == 0 ? FIX_MAX : minTradeVolume.div(price, CEIL);\n        return size > 0 ? size : 1;\n    }\n\n    /// Calculates the maximum trade size for a trade pair of tokens\n    /// @return {tok} The max trade size for the trade overall\n    function maxTradeSize(\n        IAsset sell,\n        IAsset buy,\n        uint192 price\n    ) private view returns (uint192) {\n        // D18{tok} = D18{UoA} / D18{UoA/tok}\n        uint192 size = fixMin(sell.maxTradeVolume(), buy.maxTradeVolume()).safeDiv(price, FLOOR);\n        return size > 0 ? size : 1;\n    }\n}\n"
      },
      "contracts/plugins/assets/Asset.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"../../interfaces/IAsset.sol\";\nimport \"./OracleLib.sol\";\nimport \"./VersionedAsset.sol\";\n\ncontract Asset is IAsset, VersionedAsset {\n    using FixLib for uint192;\n    using OracleLib for AggregatorV3Interface;\n\n    AggregatorV3Interface public immutable chainlinkFeed; // {UoA/tok}\n\n    IERC20Metadata public immutable erc20;\n\n    uint8 public immutable erc20Decimals;\n\n    uint192 public immutable override maxTradeVolume; // {UoA}\n\n    uint48 public immutable oracleTimeout; // {s}\n\n    uint192 public immutable oracleError; // {1}\n\n    // === Lot price ===\n\n    uint48 public immutable priceTimeout; // {s} The period over which `savedHighPrice` decays to 0\n\n    uint192 public savedLowPrice; // {UoA/tok} The low price of the token during the last update\n\n    uint192 public savedHighPrice; // {UoA/tok} The high price of the token during the last update\n\n    uint48 public lastSave; // {s} The timestamp when prices were last saved\n\n    /// @param priceTimeout_ {s} The number of seconds over which savedHighPrice decays to 0\n    /// @param chainlinkFeed_ Feed units: {UoA/tok}\n    /// @param oracleError_ {1} The % the oracle feed can be off by\n    /// @param maxTradeVolume_ {UoA} The max trade volume, in UoA\n    /// @param oracleTimeout_ {s} The number of seconds until a oracle value becomes invalid\n    /// @dev oracleTimeout_ is also used as the timeout value in lotPrice(), should be highest of\n    ///      all assets' oracleTimeout in a collateral if there are multiple oracles\n    constructor(\n        uint48 priceTimeout_,\n        AggregatorV3Interface chainlinkFeed_,\n        uint192 oracleError_,\n        IERC20Metadata erc20_,\n        uint192 maxTradeVolume_,\n        uint48 oracleTimeout_\n    ) {\n        require(priceTimeout_ > 0, \"price timeout zero\");\n        require(address(chainlinkFeed_) != address(0), \"missing chainlink feed\");\n        require(oracleError_ > 0 && oracleError_ < FIX_ONE, \"oracle error out of range\");\n        require(address(erc20_) != address(0), \"missing erc20\");\n        require(maxTradeVolume_ > 0, \"invalid max trade volume\");\n        require(oracleTimeout_ > 0, \"oracleTimeout zero\");\n        priceTimeout = priceTimeout_;\n        chainlinkFeed = chainlinkFeed_;\n        oracleError = oracleError_;\n        erc20 = erc20_;\n        erc20Decimals = erc20.decimals();\n        maxTradeVolume = maxTradeVolume_;\n        oracleTimeout = oracleTimeout_;\n    }\n\n    /// Can revert, used by other contract functions in order to catch errors\n    /// Should not return FIX_MAX for low\n    /// Should only return FIX_MAX for high if low is 0\n    /// @dev The third (unused) variable is only here for compatibility with Collateral\n    /// @return low {UoA/tok} The low price estimate\n    /// @return high {UoA/tok} The high price estimate\n    function tryPrice()\n        external\n        view\n        virtual\n        returns (\n            uint192 low,\n            uint192 high,\n            uint192\n        )\n    {\n        uint192 p = chainlinkFeed.price(oracleTimeout); // {UoA/tok}\n        uint192 err = p.mul(oracleError, CEIL);\n        // assert(low <= high); obviously true just by inspection\n        return (p - err, p + err, 0);\n    }\n\n    /// Should not revert\n    /// Refresh saved prices\n    function refresh() public virtual override {\n        try this.tryPrice() returns (uint192 low, uint192 high, uint192) {\n            // {UoA/tok}, {UoA/tok}\n            // (0, 0) is a valid price; (0, FIX_MAX) is unpriced\n\n            // Save prices if priced\n            if (high < FIX_MAX) {\n                savedLowPrice = low;\n                savedHighPrice = high;\n                lastSave = uint48(block.timestamp);\n            } else {\n                // must be unpriced\n                assert(low == 0);\n            }\n        } catch (bytes memory errData) {\n            // see: docs/solidity-style.md#Catching-Empty-Data\n            if (errData.length == 0) revert(); // solhint-disable-line reason-string\n        }\n    }\n\n    /// Should not revert\n    /// @dev Should be general enough to not need to be overridden\n    /// @return {UoA/tok} The lower end of the price estimate\n    /// @return {UoA/tok} The upper end of the price estimate\n    function price() public view virtual returns (uint192, uint192) {\n        try this.tryPrice() returns (uint192 low, uint192 high, uint192) {\n            assert(low <= high);\n            return (low, high);\n        } catch (bytes memory errData) {\n            // see: docs/solidity-style.md#Catching-Empty-Data\n            if (errData.length == 0) revert(); // solhint-disable-line reason-string\n            return (0, FIX_MAX);\n        }\n    }\n\n    /// Should not revert\n    /// lotLow should be nonzero when the asset might be worth selling\n    /// @dev Should be general enough to not need to be overridden\n    /// @return lotLow {UoA/tok} The lower end of the lot price estimate\n    /// @return lotHigh {UoA/tok} The upper end of the lot price estimate\n    function lotPrice() external view virtual returns (uint192 lotLow, uint192 lotHigh) {\n        try this.tryPrice() returns (uint192 low, uint192 high, uint192) {\n            // if the price feed is still functioning, use that\n            lotLow = low;\n            lotHigh = high;\n        } catch (bytes memory errData) {\n            // see: docs/solidity-style.md#Catching-Empty-Data\n            if (errData.length == 0) revert(); // solhint-disable-line reason-string\n\n            // if the price feed is broken, use a decayed historical value\n\n            uint48 delta = uint48(block.timestamp) - lastSave; // {s}\n            if (delta <= oracleTimeout) {\n                lotLow = savedLowPrice;\n                lotHigh = savedHighPrice;\n            } else if (delta >= oracleTimeout + priceTimeout) {\n                return (0, 0); // no price after full timeout\n            } else {\n                // oracleTimeout <= delta <= oracleTimeout + priceTimeout\n\n                // {1} = {s} / {s}\n                uint192 lotMultiplier = divuu(oracleTimeout + priceTimeout - delta, priceTimeout);\n\n                // {UoA/tok} = {UoA/tok} * {1}\n                lotLow = savedLowPrice.mul(lotMultiplier);\n                lotHigh = savedHighPrice.mul(lotMultiplier);\n            }\n        }\n        assert(lotLow <= lotHigh);\n    }\n\n    /// @return {tok} The balance of the ERC20 in whole tokens\n    function bal(address account) external view virtual returns (uint192) {\n        return shiftl_toFix(erc20.balanceOf(account), -int8(erc20Decimals));\n    }\n\n    /// @return If the asset is an instance of ICollateral or not\n    function isCollateral() external pure virtual returns (bool) {\n        return false;\n    }\n\n    // solhint-disable no-empty-blocks\n\n    /// Claim rewards earned by holding a balance of the ERC20 token\n    /// DEPRECATED: claimRewards() will be removed from all assets and collateral plugins\n    function claimRewards() external virtual {}\n\n    // solhint-enable no-empty-blocks\n}\n"
      },
      "contracts/plugins/assets/OracleLib.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport \"../../libraries/Fixed.sol\";\n\nerror StalePrice();\nerror ZeroPrice();\n\ninterface EACAggregatorProxy {\n    function aggregator() external view returns (address);\n}\n\n/// Used by asset plugins to price their collateral\nlibrary OracleLib {\n    /// @dev Use for nested calls that should revert when there is a problem\n    /// @param timeout The number of seconds after which oracle values should be considered stale\n    /// @return {UoA/tok}\n    function price(AggregatorV3Interface chainlinkFeed, uint48 timeout)\n        internal\n        view\n        returns (uint192)\n    {\n        try chainlinkFeed.latestRoundData() returns (\n            uint80 roundId,\n            int256 p,\n            uint256,\n            uint256 updateTime,\n            uint80 answeredInRound\n        ) {\n            if (updateTime == 0 || answeredInRound < roundId) {\n                revert StalePrice();\n            }\n\n            // Downcast is safe: uint256(-) reverts on underflow; block.timestamp assumed < 2^48\n            uint48 secondsSince = uint48(block.timestamp - updateTime);\n            if (secondsSince > timeout) revert StalePrice();\n\n            if (p == 0) revert ZeroPrice();\n\n            // {UoA/tok}\n            return shiftl_toFix(uint256(p), -int8(chainlinkFeed.decimals()));\n        } catch (bytes memory errData) {\n            // Check if the aggregator was not set: if so, the chainlink feed has been deprecated\n            // and a _specific_ error needs to be raised in order to avoid looking like OOG\n            if (errData.length == 0) {\n                if (EACAggregatorProxy(address(chainlinkFeed)).aggregator() == address(0)) {\n                    revert StalePrice();\n                }\n                // solhint-disable-next-line reason-string\n                revert();\n            }\n\n            // Otherwise, preserve the error bytes\n            // solhint-disable-next-line no-inline-assembly\n            assembly {\n                revert(add(32, errData), mload(errData))\n            }\n        }\n    }\n}\n"
      },
      "contracts/plugins/assets/RTokenAsset.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"../../p1/mixins/RecollateralizationLib.sol\";\nimport \"../../interfaces/IMain.sol\";\nimport \"../../interfaces/IRToken.sol\";\nimport \"../../interfaces/IRTokenOracle.sol\";\nimport \"./Asset.sol\";\nimport \"./VersionedAsset.sol\";\n\nuint256 constant ORACLE_TIMEOUT = 15 minutes;\n\n/// Once an RToken gets large enough to get a price feed, replacing this asset with\n/// a simpler one will do wonders for gas usage\n/// @dev This RTokenAsset is ONLY compatible with Protocol ^3.0.0\ncontract RTokenAsset is IAsset, VersionedAsset, IRTokenOracle {\n    using FixLib for uint192;\n    using OracleLib for AggregatorV3Interface;\n\n    // Component addresses are not mutable in protocol, so it's safe to cache these\n    IMain public immutable main;\n    IBasketHandler public immutable basketHandler;\n    IAssetRegistry public immutable assetRegistry;\n    IBackingManager public immutable backingManager;\n\n    IERC20Metadata public immutable erc20;\n\n    uint8 public immutable erc20Decimals;\n\n    uint192 public immutable maxTradeVolume; // {UoA}\n\n    // Oracle State\n    CachedOracleData public cachedOracleData;\n\n    /// @param maxTradeVolume_ {UoA} The max trade volume, in UoA\n    constructor(IRToken erc20_, uint192 maxTradeVolume_) {\n        require(address(erc20_) != address(0), \"missing erc20\");\n        require(maxTradeVolume_ > 0, \"invalid max trade volume\");\n\n        main = erc20_.main();\n        basketHandler = main.basketHandler();\n        assetRegistry = main.assetRegistry();\n        backingManager = main.backingManager();\n\n        erc20 = IERC20Metadata(address(erc20_));\n        erc20Decimals = erc20_.decimals();\n        maxTradeVolume = maxTradeVolume_;\n    }\n\n    /// Can revert, used by other contract functions in order to catch errors\n    /// @dev This method for calculating the price can provide a 2x larger range than the average\n    ///   oracleError of the RToken's backing collateral. This only occurs when there is\n    ///   less RSR overcollateralization in % terms than the average (weighted) oracleError.\n    ///   This arises from the use of oracleErrors inside of `basketRange()` and inside\n    ///   `basketHandler.price()`. When `range.bottom == range.top` then there is no compounding.\n    /// @return low {UoA/tok} The low price estimate\n    /// @return high {UoA/tok} The high price estimate\n    function tryPrice(bool useLotPrice) external view virtual returns (uint192 low, uint192 high) {\n        (uint192 lowBUPrice, uint192 highBUPrice) = useLotPrice\n            ? basketHandler.lotPrice()\n            : basketHandler.price(); // {UoA/BU}\n        require(lowBUPrice != 0 && highBUPrice != FIX_MAX, \"invalid price\");\n        assert(lowBUPrice <= highBUPrice); // not obviously true just by inspection\n\n        // Here we take advantage of the fact that we know RToken has 18 decimals\n        // to convert between uint256 an uint192. Fits due to assumed max totalSupply.\n        uint192 supply = _safeWrap(IRToken(address(erc20)).totalSupply());\n\n        if (supply == 0) return (lowBUPrice, highBUPrice);\n\n        // The RToken's price is not symmetric like other assets!\n        // range.bottom is lower because of the slippage from the shortfall\n        BasketRange memory range = basketRange(); // {BU}\n\n        // {UoA/tok} = {BU} * {UoA/BU} / {tok}\n        low = range.bottom.mulDiv(lowBUPrice, supply, FLOOR);\n        high = range.top.mulDiv(highBUPrice, supply, CEIL);\n\n        assert(low <= high); // not obviously true\n    }\n\n    // solhint-disable no-empty-blocks\n    function refresh() public virtual override {\n        // No need to save lastPrice; can piggyback off the backing collateral's lotPrice()\n\n        cachedOracleData.cachedAtTime = 0; // force oracle refresh\n    }\n\n    // solhint-enable no-empty-blocks\n\n    /// Should not revert\n    /// @dev See `tryPrice` caveat about possible compounding error in calculating price\n    /// @return {UoA/tok} The lower end of the price estimate\n    /// @return {UoA/tok} The upper end of the price estimate\n    function price() public view virtual returns (uint192, uint192) {\n        try this.tryPrice(false) returns (uint192 low, uint192 high) {\n            return (low, high);\n        } catch (bytes memory errData) {\n            // see: docs/solidity-style.md#Catching-Empty-Data\n            if (errData.length == 0) revert(); // solhint-disable-line reason-string\n            return (0, FIX_MAX);\n        }\n    }\n\n    /// Should not revert\n    /// lotLow should be nonzero when the asset might be worth selling\n    /// @dev See `tryPrice` caveat about possible compounding error in calculating price\n    /// @return lotLow {UoA/tok} The lower end of the lot price estimate\n    /// @return lotHigh {UoA/tok} The upper end of the lot price estimate\n    function lotPrice() external view returns (uint192 lotLow, uint192 lotHigh) {\n        try this.tryPrice(true) returns (uint192 low, uint192 high) {\n            lotLow = low;\n            lotHigh = high;\n        } catch (bytes memory errData) {\n            // see: docs/solidity-style.md#Catching-Empty-Data\n            if (errData.length == 0) revert(); // solhint-disable-line reason-string\n            return (0, 0);\n        }\n    }\n\n    /// @return {tok} The balance of the ERC20 in whole tokens\n    function bal(address account) external view returns (uint192) {\n        // The RToken has 18 decimals, so there's no reason to waste gas here doing a shiftl_toFix\n        // return shiftl_toFix(erc20.balanceOf(account), -int8(erc20Decimals));\n        return _safeWrap(erc20.balanceOf(account));\n    }\n\n    /// @return {s} The timestamp of the last refresh; always 0 since prices are never saved\n    function lastSave() external pure returns (uint48) {\n        return 0;\n    }\n\n    /// @return If the asset is an instance of ICollateral or not\n    function isCollateral() external pure virtual returns (bool) {\n        return false;\n    }\n\n    // solhint-disable no-empty-blocks\n\n    /// Claim rewards earned by holding a balance of the ERC20 token\n    /// DEPRECATED: claimRewards() will be removed from all assets and collateral plugins\n    function claimRewards() external virtual {}\n\n    // solhint-enable no-empty-blocks\n\n    function forceUpdatePrice() external {\n        _updateCachedPrice();\n    }\n\n    function latestPrice() external returns (uint192 rTokenPrice, uint256 updatedAt) {\n        // Situations that require an update, from most common to least common.\n        if (\n            cachedOracleData.cachedAtTime + ORACLE_TIMEOUT <= block.timestamp || // Cache Timeout\n            cachedOracleData.cachedAtNonce != basketHandler.nonce() || // Basket nonce was updated\n            cachedOracleData.cachedTradesNonce != backingManager.tradesNonce() || // New trades\n            cachedOracleData.cachedTradesOpen != backingManager.tradesOpen() // ..or settled\n        ) {\n            _updateCachedPrice();\n        }\n\n        return (cachedOracleData.cachedPrice, cachedOracleData.cachedAtTime);\n    }\n\n    // ==== Private ====\n\n    // Update Oracle Data\n    function _updateCachedPrice() internal {\n        (uint192 low, uint192 high) = price();\n\n        require(low != 0 && high != FIX_MAX, \"invalid price\");\n\n        cachedOracleData = CachedOracleData(\n            (low + high) / 2,\n            block.timestamp,\n            basketHandler.nonce(),\n            backingManager.tradesOpen(),\n            backingManager.tradesNonce()\n        );\n    }\n\n    /// Computationally expensive basketRange calculation; used in price() & lotPrice()\n    function basketRange() private view returns (BasketRange memory range) {\n        BasketRange memory basketsHeld = basketHandler.basketsHeldBy(address(backingManager));\n        uint192 basketsNeeded = IRToken(address(erc20)).basketsNeeded(); // {BU}\n\n        // if (basketHandler.fullyCollateralized())\n        if (basketsHeld.bottom >= basketsNeeded) {\n            range.bottom = basketsNeeded;\n            range.top = basketsNeeded;\n        } else {\n            // Note: Extremely this is extremely wasteful in terms of gas. This only exists so\n            // there is _some_ asset to represent the RToken itself when it is deployed, in\n            // the absence of an external price feed. Any RToken that gets reasonably big\n            // should switch over to an asset with a price feed.\n\n            TradingContext memory ctx;\n\n            ctx.basketsHeld = basketsHeld;\n            ctx.bm = backingManager;\n            ctx.bh = basketHandler;\n            ctx.ar = assetRegistry;\n            ctx.stRSR = main.stRSR();\n            ctx.rsr = main.rsr();\n            ctx.rToken = main.rToken();\n            ctx.minTradeVolume = backingManager.minTradeVolume();\n            ctx.maxTradeSlippage = backingManager.maxTradeSlippage();\n\n            // Calculate quantities\n            Registry memory reg = ctx.ar.getRegistry();\n            ctx.quantities = new uint192[](reg.erc20s.length);\n            for (uint256 i = 0; i < reg.erc20s.length; ++i) {\n                ctx.quantities[i] = ctx.bh.quantityUnsafe(reg.erc20s[i], reg.assets[i]);\n            }\n\n            // will exclude UoA value from RToken balances at BackingManager\n            range = RecollateralizationLibP1.basketRange(ctx, reg);\n        }\n    }\n}\n"
      },
      "contracts/plugins/assets/VersionedAsset.sol": {
        "content": "// SPDX-License-Identifier: BlueOak-1.0.0\npragma solidity 0.8.19;\n\nimport \"../../interfaces/IVersioned.sol\";\n\n// This value should be updated on each release\nstring constant ASSET_VERSION = \"3.0.1\";\n\n/**\n * @title VersionedAsset\n * @notice A mix-in to track semantic versioning uniformly across asset plugin contracts.\n */\nabstract contract VersionedAsset is IVersioned {\n    function version() public pure virtual override returns (string memory) {\n        return ASSET_VERSION;\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}