{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/access/IAccessControl.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 IAccessControl {\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/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.9.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(address from, address to, uint256 amount) external returns (bool);\n}\n"
    },
    "@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport \"@openzeppelin/contracts/access/IAccessControl.sol\";\n\n/**\n * @title IAccessControlManagerV8\n * @author Venus\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\n */\ninterface IAccessControlManagerV8 is IAccessControl {\n    function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\n\n    function revokeCallPermission(\n        address contractAddress,\n        string calldata functionSig,\n        address accountToRevoke\n    ) external;\n\n    function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\n\n    function hasPermission(\n        address account,\n        address contractAddress,\n        string calldata functionSig\n    ) external view returns (bool);\n}\n"
    },
    "@venusprotocol/solidity-utilities/contracts/constants.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n"
    },
    "@venusprotocol/solidity-utilities/contracts/validators.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\nerror ZeroAddressNotAllowed();\n\n/// @notice Thrown if the supplied value is 0 where it is not allowed\nerror ZeroValueNotAllowed();\n\n/// @notice Checks if the provided address is nonzero, reverts otherwise\n/// @param address_ Address to check\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\nfunction ensureNonzeroAddress(address address_) pure {\n    if (address_ == address(0)) {\n        revert ZeroAddressNotAllowed();\n    }\n}\n\n/// @notice Checks if the provided value is nonzero, reverts otherwise\n/// @param value_ Value to check\n/// @custom:error ZeroValueNotAllowed is thrown if the provided value is 0\nfunction ensureNonzeroValue(uint256 value_) pure {\n    if (value_ == 0) {\n        revert ZeroValueNotAllowed();\n    }\n}\n"
    },
    "contracts/interfaces/ICappedOracle.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\ninterface ICappedOracle {\n    function updateSnapshot() external;\n}\n"
    },
    "contracts/interfaces/IStETH.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IStETH {\n    function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);\n    function decimals() external view returns (uint8);\n}\n"
    },
    "contracts/interfaces/OracleInterface.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n    function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n    function updatePrice(address vToken) external;\n\n    function updateAssetPrice(address asset) external;\n\n    function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n    function validatePriceWithAnchorPrice(\n        address asset,\n        uint256 reporterPrice,\n        uint256 anchorPrice\n    ) external view returns (bool);\n}\n"
    },
    "contracts/oracles/common/CorrelatedTokenOracle.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { OracleInterface, ResilientOracleInterface } from \"../../interfaces/OracleInterface.sol\";\nimport { ensureNonzeroAddress } from \"@venusprotocol/solidity-utilities/contracts/validators.sol\";\nimport { SECONDS_PER_YEAR } from \"@venusprotocol/solidity-utilities/contracts/constants.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { ICappedOracle } from \"../../interfaces/ICappedOracle.sol\";\nimport { IAccessControlManagerV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol\";\n\n/**\n * @title CorrelatedTokenOracle\n * @notice This oracle fetches the price of a token that is correlated to another token.\n */\nabstract contract CorrelatedTokenOracle is OracleInterface, ICappedOracle {\n    /// @notice Address of the correlated token\n    address public immutable CORRELATED_TOKEN;\n\n    /// @notice Address of the underlying token\n    address public immutable UNDERLYING_TOKEN;\n\n    /// @notice Address of Resilient Oracle\n    ResilientOracleInterface public immutable RESILIENT_ORACLE;\n\n    /// @notice Address of the AccessControlManager contract\n    IAccessControlManagerV8 public immutable ACCESS_CONTROL_MANAGER;\n\n    //// @notice Growth rate percentage in seconds. Ex: 1e18 is 100%\n    uint256 public growthRatePerSecond;\n\n    /// @notice Snapshot update interval\n    uint256 public snapshotInterval;\n\n    /// @notice Last stored snapshot maximum exchange rate\n    uint256 public snapshotMaxExchangeRate;\n\n    /// @notice Last stored snapshot timestamp\n    uint256 public snapshotTimestamp;\n\n    /// @notice Gap to add when updating the snapshot\n    uint256 public snapshotGap;\n\n    /// @notice Emitted when the snapshot is updated\n    event SnapshotUpdated(uint256 indexed maxExchangeRate, uint256 indexed timestamp);\n\n    /// @notice Emitted when the growth rate is updated\n    event GrowthRateUpdated(\n        uint256 indexed oldGrowthRatePerSecond,\n        uint256 indexed newGrowthRatePerSecond,\n        uint256 indexed oldSnapshotInterval,\n        uint256 newSnapshotInterval\n    );\n\n    /// @notice Emitted when the snapshot gap is updated\n    event SnapshotGapUpdated(uint256 indexed oldSnapshotGap, uint256 indexed newSnapshotGap);\n\n    /// @notice Thrown if the token address is invalid\n    error InvalidTokenAddress();\n\n    /// @notice Thrown if the growth rate is invalid\n    error InvalidGrowthRate();\n\n    /// @notice Thrown if the initial snapshot is invalid\n    error InvalidInitialSnapshot();\n\n    /// @notice Thrown if the max snapshot exchange rate is invalid\n    error InvalidSnapshotMaxExchangeRate();\n\n    /// @notice @notice Thrown when the action is prohibited by AccessControlManager\n    error Unauthorized(address sender, address calledContract, string methodSignature);\n\n    /**\n     * @notice Constructor for the implementation contract.\n     * @custom:error InvalidGrowthRate error is thrown if the growth rate is invalid\n     * @custom:error InvalidInitialSnapshot error is thrown if the initial snapshot values are invalid\n     */\n    constructor(\n        address _correlatedToken,\n        address _underlyingToken,\n        address _resilientOracle,\n        uint256 _annualGrowthRate,\n        uint256 _snapshotInterval,\n        uint256 _initialSnapshotMaxExchangeRate,\n        uint256 _initialSnapshotTimestamp,\n        address _accessControlManager,\n        uint256 _snapshotGap\n    ) {\n        growthRatePerSecond = _annualGrowthRate / SECONDS_PER_YEAR;\n\n        if ((growthRatePerSecond == 0 && _snapshotInterval > 0) || (growthRatePerSecond > 0 && _snapshotInterval == 0))\n            revert InvalidGrowthRate();\n\n        if ((_initialSnapshotMaxExchangeRate == 0 || _initialSnapshotTimestamp == 0) && _snapshotInterval > 0) {\n            revert InvalidInitialSnapshot();\n        }\n\n        ensureNonzeroAddress(_correlatedToken);\n        ensureNonzeroAddress(_underlyingToken);\n        ensureNonzeroAddress(_resilientOracle);\n        ensureNonzeroAddress(_accessControlManager);\n\n        CORRELATED_TOKEN = _correlatedToken;\n        UNDERLYING_TOKEN = _underlyingToken;\n        RESILIENT_ORACLE = ResilientOracleInterface(_resilientOracle);\n        snapshotInterval = _snapshotInterval;\n\n        snapshotMaxExchangeRate = _initialSnapshotMaxExchangeRate;\n        snapshotTimestamp = _initialSnapshotTimestamp;\n        snapshotGap = _snapshotGap;\n\n        ACCESS_CONTROL_MANAGER = IAccessControlManagerV8(_accessControlManager);\n    }\n\n    /**\n     * @notice Directly sets the snapshot exchange rate and timestamp\n     * @param _snapshotMaxExchangeRate The exchange rate to set\n     * @param _snapshotTimestamp The timestamp to set\n     * @custom:event Emits SnapshotUpdated event on successful update of the snapshot\n     */\n    function setSnapshot(uint256 _snapshotMaxExchangeRate, uint256 _snapshotTimestamp) external {\n        _checkAccessAllowed(\"setSnapshot(uint256,uint256)\");\n\n        snapshotMaxExchangeRate = _snapshotMaxExchangeRate;\n        snapshotTimestamp = _snapshotTimestamp;\n\n        emit SnapshotUpdated(snapshotMaxExchangeRate, snapshotTimestamp);\n    }\n\n    /**\n     * @notice Sets the growth rate and snapshot interval\n     * @param _annualGrowthRate The annual growth rate to set\n     * @param _snapshotInterval The snapshot interval to set\n     * @custom:error InvalidGrowthRate error is thrown if the growth rate is invalid\n     * @custom:event Emits GrowthRateUpdated event on successful update of the growth rate\n     */\n    function setGrowthRate(uint256 _annualGrowthRate, uint256 _snapshotInterval) external {\n        _checkAccessAllowed(\"setGrowthRate(uint256,uint256)\");\n        uint256 oldGrowthRatePerSecond = growthRatePerSecond;\n\n        growthRatePerSecond = _annualGrowthRate / SECONDS_PER_YEAR;\n\n        if ((growthRatePerSecond == 0 && _snapshotInterval > 0) || (growthRatePerSecond > 0 && _snapshotInterval == 0))\n            revert InvalidGrowthRate();\n\n        emit GrowthRateUpdated(oldGrowthRatePerSecond, growthRatePerSecond, snapshotInterval, _snapshotInterval);\n\n        snapshotInterval = _snapshotInterval;\n    }\n\n    /**\n     * @notice Sets the snapshot gap\n     * @param _snapshotGap The snapshot gap to set\n     * @custom:event Emits SnapshotGapUpdated event on successful update of the snapshot gap\n     */\n    function setSnapshotGap(uint256 _snapshotGap) external {\n        _checkAccessAllowed(\"setSnapshotGap(uint256)\");\n\n        emit SnapshotGapUpdated(snapshotGap, _snapshotGap);\n\n        snapshotGap = _snapshotGap;\n    }\n\n    /**\n     * @notice Returns if the price is capped\n     * @return isCapped Boolean indicating if the price is capped\n     */\n    function isCapped() external view virtual returns (bool) {\n        if (snapshotInterval == 0) {\n            return false;\n        }\n\n        uint256 maxAllowedExchangeRate = getMaxAllowedExchangeRate();\n        if (maxAllowedExchangeRate == 0) {\n            return false;\n        }\n\n        uint256 exchangeRate = getUnderlyingAmount();\n\n        return exchangeRate > maxAllowedExchangeRate;\n    }\n\n    /**\n     * @notice Updates the snapshot price and timestamp\n     * @custom:event Emits SnapshotUpdated event on successful update of the snapshot\n     * @custom:error InvalidSnapshotMaxExchangeRate error is thrown if the max snapshot exchange rate is zero\n     */\n    function updateSnapshot() public override {\n        if (block.timestamp - snapshotTimestamp < snapshotInterval || snapshotInterval == 0) return;\n\n        uint256 exchangeRate = getUnderlyingAmount();\n        uint256 maxAllowedExchangeRate = getMaxAllowedExchangeRate();\n\n        snapshotMaxExchangeRate =\n            (exchangeRate > maxAllowedExchangeRate ? maxAllowedExchangeRate : exchangeRate) +\n            snapshotGap;\n        snapshotTimestamp = block.timestamp;\n\n        if (snapshotMaxExchangeRate == 0) revert InvalidSnapshotMaxExchangeRate();\n\n        RESILIENT_ORACLE.updateAssetPrice(UNDERLYING_TOKEN);\n        emit SnapshotUpdated(snapshotMaxExchangeRate, snapshotTimestamp);\n    }\n\n    /**\n     * @notice Fetches the price of the token\n     * @param asset Address of the token\n     * @return price The price of the token in scaled decimal places. It can be capped\n     * to a maximum value taking into account the growth rate\n     * @custom:error InvalidTokenAddress error is thrown if the token address is invalid\n     */\n    function getPrice(address asset) public view override returns (uint256) {\n        if (asset != CORRELATED_TOKEN) revert InvalidTokenAddress();\n\n        uint256 exchangeRate = getUnderlyingAmount();\n\n        if (snapshotInterval == 0) {\n            return _calculatePrice(exchangeRate);\n        }\n\n        uint256 maxAllowedExchangeRate = getMaxAllowedExchangeRate();\n\n        uint256 finalExchangeRate = (exchangeRate > maxAllowedExchangeRate && maxAllowedExchangeRate != 0)\n            ? maxAllowedExchangeRate\n            : exchangeRate;\n\n        return _calculatePrice(finalExchangeRate);\n    }\n\n    /**\n     * @notice Gets the maximum allowed exchange rate for token\n     * @return maxExchangeRate Maximum allowed exchange rate\n     */\n    function getMaxAllowedExchangeRate() public view returns (uint256) {\n        uint256 timeElapsed = block.timestamp - snapshotTimestamp;\n        uint256 maxExchangeRate = snapshotMaxExchangeRate +\n            (snapshotMaxExchangeRate * growthRatePerSecond * timeElapsed) /\n            1e18;\n        return maxExchangeRate;\n    }\n\n    /**\n     * @notice Gets the underlying amount for correlated token\n     * @return underlyingAmount Amount of underlying token\n     */\n    function getUnderlyingAmount() public view virtual returns (uint256);\n\n    /**\n     * @notice Fetches price of the token based on an underlying exchange rate\n     * @param exchangeRate The underlying exchange rate to use\n     * @return price The price of the token in scaled decimal places\n     */\n    function _calculatePrice(uint256 exchangeRate) internal view returns (uint256) {\n        uint256 underlyingUSDPrice = RESILIENT_ORACLE.getPrice(UNDERLYING_TOKEN);\n\n        IERC20Metadata token = IERC20Metadata(CORRELATED_TOKEN);\n        uint256 decimals = token.decimals();\n\n        return (exchangeRate * underlyingUSDPrice) / (10 ** decimals);\n    }\n\n    /**\n     * @notice Reverts if the call is not allowed by AccessControlManager\n     * @param signature Method signature\n     * @custom:error Unauthorized error is thrown if the call is not allowed\n     */\n    function _checkAccessAllowed(string memory signature) internal view {\n        bool isAllowedToCall = ACCESS_CONTROL_MANAGER.isAllowedToCall(msg.sender, signature);\n\n        if (!isAllowedToCall) {\n            revert Unauthorized(msg.sender, address(this), signature);\n        }\n    }\n}\n"
    },
    "contracts/oracles/WstETHOracleV2.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IStETH } from \"../interfaces/IStETH.sol\";\nimport { CorrelatedTokenOracle } from \"./common/CorrelatedTokenOracle.sol\";\nimport { EXP_SCALE } from \"@venusprotocol/solidity-utilities/contracts/constants.sol\";\nimport { ensureNonzeroAddress } from \"@venusprotocol/solidity-utilities/contracts/validators.sol\";\n\n/**\n * @title WstETHOracleV2\n * @author Venus\n * @notice This oracle fetches the price of wstETH\n */\ncontract WstETHOracleV2 is CorrelatedTokenOracle {\n    /// @notice Address of stETH\n    IStETH public immutable STETH;\n\n    /// @notice Constructor for the implementation contract.\n    /// @dev The underlyingToken must be correlated so that 1 underlyingToken is equal to 1 stETH, because\n    /// getUnderlyingAmount() implicitly assumes that\n    constructor(\n        address stETH,\n        address wstETH,\n        address underlyingToken,\n        address resilientOracle,\n        uint256 annualGrowthRate,\n        uint256 _snapshotInterval,\n        uint256 initialSnapshotMaxExchangeRate,\n        uint256 initialSnapshotTimestamp,\n        address accessControlManager,\n        uint256 _snapshotGap\n    )\n        CorrelatedTokenOracle(\n            wstETH,\n            underlyingToken,\n            resilientOracle,\n            annualGrowthRate,\n            _snapshotInterval,\n            initialSnapshotMaxExchangeRate,\n            initialSnapshotTimestamp,\n            accessControlManager,\n            _snapshotGap\n        )\n    {\n        ensureNonzeroAddress(stETH);\n        STETH = IStETH(stETH);\n    }\n\n    /**\n     * @notice Gets the amount of underlyingToken for 1 wstETH, assuming that 1 underlyingToken is equivalent to 1 stETH\n     * @return amount Amount of underlyingToken\n     */\n    function getUnderlyingAmount() public view override returns (uint256) {\n        return STETH.getPooledEthByShares(EXP_SCALE);\n    }\n}\n"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200,
      "details": {
        "yul": true
      }
    },
    "evmVersion": "cancun",
    "outputSelection": {
      "*": {
        "*": [
          "storageLayout",
          "abi",
          "evm.bytecode",
          "evm.deployedBytecode",
          "evm.methodIdentifiers",
          "metadata",
          "devdoc",
          "userdoc",
          "evm.gasEstimates"
        ],
        "": ["ast"]
      }
    },
    "metadata": {
      "useLiteralContent": true
    }
  }
}
