// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.19; import {Harberg} from "./Harberg.sol"; import {Stake} from "./Stake.sol"; import {UUPSUpgradeable} from "@openzeppelin/proxy/utils/UUPSUpgradeable.sol"; import {Initializable} from "@openzeppelin/proxy/utils/Initializable.sol"; /** * @title Optimizer * @notice This contract (formerly Sentimenter) calculates a “sentiment” value and liquidity parameters * based on the tax rate and the percentage of Harberg staked. * @dev It is upgradeable using UUPS. Only the admin (set during initialization) can upgrade. */ contract Optimizer is Initializable, UUPSUpgradeable { Harberg private harberg; Stake private stake; /// @dev Reverts if the caller is not the admin. error UnauthorizedAccount(address account); /** * @notice Initialize the Optimizer. * @param _harberg The address of the Harberg token. * @param _stake The address of the Stake contract. */ function initialize(address _harberg, address _stake) initializer public { // Set the admin for upgradeability (using ERC1967Upgrade _changeAdmin) _changeAdmin(msg.sender); harberg = Harberg(_harberg); stake = Stake(_stake); } modifier onlyAdmin() { _checkAdmin(); _; } function _checkAdmin() internal view virtual { if (_getAdmin() != msg.sender) { revert UnauthorizedAccount(msg.sender); } } function _authorizeUpgrade(address newImplementation) internal override onlyAdmin {} /** * @notice Calculates the sentiment based on the average tax rate and the percentage staked. * @param averageTaxRate The average tax rate (as returned by the Stake contract). * @param percentageStaked The percentage (in 1e18 precision) of the authorized stake that is currently staked. * @return sentimentValue A value in the range 0 to 1e18 where 1e18 represents the worst sentiment. */ function calculateSentiment( uint256 averageTaxRate, uint256 percentageStaked ) public pure returns (uint256 sentimentValue) { // deltaS is the “slack” available below full staking uint256 deltaS = 1e18 - percentageStaked; if (percentageStaked > 92e16) { // If more than 92% of the authorized stake is in use, the sentiment drops rapidly. // Penalty is computed as: (deltaS^3 * averageTaxRate) / (20 * 1e48) uint256 penalty = (deltaS * deltaS * deltaS * averageTaxRate) / (20 * 1e48); sentimentValue = penalty / 2; } else { // For lower staked percentages, sentiment decreases roughly linearly. uint256 baseSentiment = 1e18 - ((percentageStaked * 1e18) / (92e16)); // Apply a penalty based on the average tax rate. if (averageTaxRate <= 1e16) { sentimentValue = baseSentiment; } else if (averageTaxRate <= 5e16) { uint256 ratePenalty = ((averageTaxRate - 1e16) * baseSentiment) / (4e16); sentimentValue = baseSentiment > ratePenalty ? baseSentiment - ratePenalty : 0; } else { // For very high tax rates, sentiment is maximally poor. sentimentValue = 1e18; } } return sentimentValue; } /** * @notice Returns the current sentiment. * @return sentiment A number (with 1e18 precision) representing the staker sentiment. */ function getSentiment() external view returns (uint256 sentiment) { uint256 percentageStaked = stake.getPercentageStaked(); uint256 averageTaxRate = stake.getAverageTaxRate(); sentiment = calculateSentiment(averageTaxRate, percentageStaked); } /** * @notice Returns liquidity parameters for the liquidity manager. * @return capitalInefficiency Calculated as (1e18 - sentiment). * @return anchorShare Set equal to the sentiment. * @return anchorWidth Here set to a constant 100 (adjust as needed). * @return discoveryDepth Set equal to the sentiment. */ function getLiquidityParams() external view returns ( uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth ) { uint256 percentageStaked = stake.getPercentageStaked(); uint256 averageTaxRate = stake.getAverageTaxRate(); uint256 sentiment = calculateSentiment(averageTaxRate, percentageStaked); capitalInefficiency = 1e18 - sentiment; anchorShare = sentiment; // Here we simply set anchorWidth to 100; adjust this formula if needed. anchorWidth = 100; discoveryDepth = sentiment; } }