Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new kerosene DV #132

Merged
merged 10 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions src/core/VaultManagerV6.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {DyadXP} from "../staking/DyadXP.sol";
import {IVaultManagerV5} from "../interfaces/IVaultManagerV5.sol";
import {DyadHooks} from "./DyadHooks.sol";
import "../interfaces/IExtension.sol";
import {KeroseneValuer} from "../staking/KeroseneValuer.sol";
import {KerosineManager} from "../core/KerosineManager.sol";

import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";
import {ERC20} from "@solmate/src/tokens/ERC20.sol";
Expand Down Expand Up @@ -47,6 +49,8 @@ contract VaultManagerV6 is IVaultManagerV5, UUPSUpgradeable, OwnableUpgradeable
/// @notice Extensions authorized by a user for use on their notes
mapping(address user => EnumerableSet.AddressSet) private _authorizedExtensions;

KeroseneValuer public keroseneValuer;

modifier isValidDNft(uint256 id) {
if (dNft.ownerOf(id) == address(0)) revert InvalidDNft();
_;
Expand All @@ -57,8 +61,12 @@ contract VaultManagerV6 is IVaultManagerV5, UUPSUpgradeable, OwnableUpgradeable
_disableInitializers();
}

function initialize() public reinitializer(6) {
// Nothing to initialize right now
function initialize(address _keroseneValuer) public reinitializer(6) {
keroseneValuer = KeroseneValuer(_keroseneValuer);
}

function setKeroseneValuer(address _newKeroseneValuer) external onlyOwner {
keroseneValuer = KeroseneValuer(_newKeroseneValuer);
}

/// @inheritdoc IVaultManagerV5
Expand Down Expand Up @@ -344,19 +352,29 @@ contract VaultManagerV6 is IVaultManagerV5, UUPSUpgradeable, OwnableUpgradeable
uint256 numberOfVaults = vaults[id].length();
vaultValues = new uint256[](numberOfVaults);

uint256 keroseneVaultIndex;
uint256 noteKeroseneAmount;

for (uint256 i = 0; i < numberOfVaults; i++) {
Vault vault = Vault(vaults[id].at(i));
if (vaultLicenser.isLicensed(address(vault))) {
uint256 value = vault.getUsdValue(id);
vaultValues[i] = value;
if (vaultLicenser.isKerosene(address(vault))) {
keroValue += value;
noteKeroseneAmount = vault.id2asset(id);
keroseneVaultIndex = i;
continue;
} else {
uint256 value = vault.getUsdValue(id);
vaultValues[i] = value;
exoValue += value;
}
}
}

if (noteKeroseneAmount > 0) {
keroValue = (noteKeroseneAmount * keroseneValuer.deterministicValue()) / 1e8;
vaultValues[keroseneVaultIndex] = keroValue;
}

mintedDyad = dyad.mintedDyad(id);
uint256 totalValue = exoValue + keroValue;
cr = _collatRatio(mintedDyad, totalValue);
Expand Down
135 changes: 135 additions & 0 deletions src/staking/KeroseneValuer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Owned} from "@solmate/src/auth/Owned.sol";
import {ERC20} from "@solmate/src/tokens/ERC20.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {Parameters} from "../params/Parameters.sol";
import {Kerosine} from "../staking/Kerosine.sol";
import {Dyad} from "../core/Dyad.sol";
import {Vault} from "../core/Vault.sol";
import {KerosineManager} from "../core/KerosineManager.sol";
import {Dyad} from "../core/Dyad.sol";

contract KeroseneValuer is Owned {
using EnumerableSet for EnumerableSet.AddressSet;
using FixedPointMathLib for uint256;

Kerosine public immutable KEROSENE;
KerosineManager public immutable KEROSENE_MANAGER;
Dyad public immutable DYAD;

uint64 public dyadMultiplierSnapshot = 1e12;
uint64 public targetDyadMultiplier = 1e12;

uint32 public dyadMultiplierSnapshotTimestamp;
uint32 public targetDyadMultiplierTimestamp;

EnumerableSet.AddressSet private _excludedAddresses;

event DyadMultiplierUpdated(uint64 previous, uint64 target, uint32 fromTimestamp, uint32 toTimestamp);

error TargetMultiplierTooSmall();

constructor(Kerosine _kerosine, KerosineManager _keroseneManager, Dyad _dyad)
Owned(0xDeD796De6a14E255487191963dEe436c45995813)
{
KEROSENE = _kerosine;
KEROSENE_MANAGER = _keroseneManager;
DYAD = _dyad;
_excludedAddresses.add(0xDeD796De6a14E255487191963dEe436c45995813); // Team Multisig
_excludedAddresses.add(0x3962f6585946823440d274aD7C719B02b49DE51E); // Sablier Linear Lockup
}

function setAddressExcluded(address _address, bool exclude) external onlyOwner {
if (exclude) {
_excludedAddresses.add(_address);
} else {
_excludedAddresses.remove(_address);
}
}

function setTargetDyadMultiplier(uint64 _targetMultiplier, uint32 _duration) external onlyOwner {
if (_targetMultiplier < 1e12) {
revert TargetMultiplierTooSmall();
}

uint64 previousMultiplier = _getDyadSupplyMultiplier();

dyadMultiplierSnapshot = previousMultiplier;
targetDyadMultiplier = _targetMultiplier;
dyadMultiplierSnapshotTimestamp = uint32(block.timestamp);
targetDyadMultiplierTimestamp = uint32(block.timestamp) + _duration;

emit DyadMultiplierUpdated(
previousMultiplier, _targetMultiplier, uint32(block.timestamp), uint32(block.timestamp) + _duration
);
}

function currentDyadMultiplier() external view returns (uint64) {
return _getDyadSupplyMultiplier();
}

function isExcludedAddress(address _address) external view returns (bool) {
return _excludedAddresses.contains(_address);
}

function excludedAddresses() external view returns (address[] memory) {
return _excludedAddresses.values();
}

function deterministicValue() external view returns (uint256) {
uint256 dyadMultiplier = _getDyadSupplyMultiplier();

uint256 normalizedSupply = DYAD.totalSupply().mulDiv(dyadMultiplier, 1e12);

uint256 tvl;

address[] memory exoVaults = KEROSENE_MANAGER.getVaults();

uint256 numberOfExoVaults = exoVaults.length;
for (uint256 i = 0; i < numberOfExoVaults; i++) {
Vault vault = Vault(exoVaults[i]);
ERC20 asset = vault.asset();
tvl += asset.balanceOf(address(vault)) * vault.assetPrice() * 1e18 / (10 ** asset.decimals())
/ (10 ** vault.oracle().decimals());
}

if (normalizedSupply >= tvl) {
return 0;
}

uint256 adjustedKerosineSupply = KEROSENE.totalSupply();
uint256 excludedAddressLength = _excludedAddresses.length();
for (uint256 i = 0; i < excludedAddressLength; ++i) {
adjustedKerosineSupply -= KEROSENE.balanceOf(_excludedAddresses.at(i));
}

return (tvl - normalizedSupply).mulDiv(1e8, adjustedKerosineSupply);
}

function _getDyadSupplyMultiplier() internal view returns (uint64) {
uint32 targetTimestamp = targetDyadMultiplierTimestamp;
if (block.timestamp >= targetTimestamp) {
return targetDyadMultiplier;
}

uint64 target = targetDyadMultiplier;
uint64 snapshot = dyadMultiplierSnapshot;
uint32 snapshotTimestamp = dyadMultiplierSnapshotTimestamp;

uint32 timeDelta = targetTimestamp - snapshotTimestamp;
uint64 multiplierDelta = target > snapshot ? target - snapshot : snapshot - target;

uint64 ratePerSecond = multiplierDelta / timeDelta;

uint32 secondsPassed = uint32(block.timestamp) - snapshotTimestamp;

if (target > snapshot) {
return snapshot + (secondsPassed * ratePerSecond);
}

return snapshot - (secondsPassed * ratePerSecond);
}
}
Loading
Loading