harb/onchain/src/Optimizer.sol

124 lines
5.2 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import {Kraiken} from "./Kraiken.sol";
import {Stake} from "./Stake.sol";
import {UUPSUpgradeable} from "@openzeppelin/proxy/utils/UUPSUpgradeable.sol";
import {Initializable} from "@openzeppelin/proxy/utils/Initializable.sol";
/**
* @title Optimizer
2025-08-18 00:16:09 +02:00
* @notice This contract (formerly Sentimenter) calculates a "sentiment" value and liquidity parameters
* based on the tax rate and the percentage of Kraiken staked.
* @dev It is upgradeable using UUPS. Only the admin (set during initialization) can upgrade.
2025-08-18 00:16:09 +02:00
*
* Key features:
* - Analyzes staking sentiment (% staked, average tax rate)
* - Returns four key parameters for liquidity management:
* 1. capitalInefficiency (0 to 1e18): Capital buffer level
* 2. anchorShare (0 to 1e18): % of non-floor ETH in anchor
* 3. anchorWidth (0 to 100): Anchor position width %
* 4. discoveryDepth (0 to 1e18): Discovery liquidity density (2x-10x)
* - Upgradeable for future algorithm improvements
*/
contract Optimizer is Initializable, UUPSUpgradeable {
Kraiken 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 Kraiken token.
* @param _stake The address of the Stake contract.
*/
2025-07-08 10:33:10 +02:00
function initialize(address _harberg, address _stake) public initializer {
// Set the admin for upgradeability (using ERC1967Upgrade _changeAdmin)
_changeAdmin(msg.sender);
harberg = Kraiken(_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.
*/
2025-07-08 10:33:10 +02:00
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.
2025-08-18 00:16:09 +02:00
* @return capitalInefficiency Calculated as (1e18 - sentiment). Capital buffer level (0-1e18)
* @return anchorShare Set equal to the sentiment. % of non-floor ETH in anchor (0-1e18)
* @return anchorWidth Here set to a constant 100. Anchor position width % (1-100)
* @return discoveryDepth Set equal to the sentiment.
*/
function getLiquidityParams()
external
view
2025-07-08 10:33:10 +02:00
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;
}
}