Skip to content

Commit

Permalink
Added oracles
Browse files Browse the repository at this point in the history
  • Loading branch information
mirooon committed Aug 3, 2024
1 parent 0fe7ed3 commit 94e72ae
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.26;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

interface IvOracle {
interface IvOracleL1 {
/**
* @notice Updates the oracle address for a given token.
* @param token The ERC20 token address whose oracle address is to be updated.
Expand Down
23 changes: 23 additions & 0 deletions src/interfaces/IvOracleL2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

interface IvOracleL2 {
/**
* @notice Updates the oracle address for the contract and verifies its decimal count.
* @param oracle The new oracle to be set, must not be a zero address and must have decimals less than or equal to 18.
*/
function updateOracle(AggregatorV3Interface oracle) external;

/**
* @notice Retrieves the current mint rate based on the oracle price, scaled to 18 decimals.
* @return scaledPrice The current scaled price of vETH.
* @return timestamp The timestamp of the latest price update.
* @dev Reverts if the latest oracle price is outdated or if the price is less than 1 Ether.
*/
function getMintRate()
external
view
returns (uint256 scaledPrice, uint256 timestamp);
}
28 changes: 13 additions & 15 deletions src/oracle/vOracle.sol → src/oracle/l1/vOracleL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IvOracle} from "../interfaces/IvOracle.sol";
import {IvOracleL1} from "../../interfaces/IvOracleL1.sol";
import {vOracleCommonConstants} from "../vOracleCommon.sol";

abstract contract vOracleConstants {
abstract contract vOracleL1Constants is vOracleCommonConstants {
/// @dev Represents a scaling factor used for all price values, defined as 10^18.
uint256 constant SCALE_FACTOR = 10 ** 18;

/// @dev Specifies the maximum allowable time interval, in seconds, for a price feed to be considered current. Beyond this duration, feeds are considered stale.
uint256 constant MAX_TIME_WINDOW = 86400; // 24 hours
}
abstract contract vOracleVariables is vOracleConstants {
abstract contract vOracleL1Variables is vOracleL1Constants {
/// @dev A mapping that links each supported ERC20 token address to its corresponding Chainlink oracle address.
mapping(IERC20 => AggregatorV3Interface) public tokenOracleLookup;
}

abstract contract vOracleErrors {
abstract contract vOracleL1Errors {
/// @dev Thrown when a function receives a zero address where a valid address is expected.
error vOracle__InvalidZeroAddress();

Expand All @@ -44,19 +42,19 @@ abstract contract vOracleErrors {
error vOracle__InvalidAmount();
}

abstract contract vOracleEvents {
abstract contract vOracleL1Events {
/// @dev Emitted when an oracle address is updated for a token.
event LogOracleUpdated(IERC20 token, AggregatorV3Interface oracleAddress);
}

contract vOracle is
IvOracle,
IvOracleL1,
Initializable,
OwnableUpgradeable,
UUPSUpgradeable,
vOracleEvents,
vOracleErrors,
vOracleVariables
vOracleL1Events,
vOracleL1Errors,
vOracleL1Variables
{
using Math for uint256;
/// @dev Prevents implementation contract from being initialized.
Expand Down Expand Up @@ -89,7 +87,7 @@ contract vOracle is
emit LogOracleUpdated(_token, _oracleAddress);
}

/// @inheritdoc IvOracle
/// @inheritdoc IvOracleL1
function lookupTokenValue(
IERC20 _token,
uint256 _balance
Expand All @@ -99,7 +97,7 @@ contract vOracle is
return uint256(price).mulDiv(_balance, SCALE_FACTOR);
}

/// @inheritdoc IvOracle
/// @inheritdoc IvOracleL1
function lookupTokenAmountFromValue(
IERC20 _token,
uint256 _value
Expand Down Expand Up @@ -165,7 +163,7 @@ contract vOracle is
return mintAmount;
}

/// @inheritdoc IvOracle
/// @inheritdoc IvOracleL1
function calculateRedeemAmount(
uint256 tokensBurned,
uint256 totalSupply,
Expand Down
86 changes: 86 additions & 0 deletions src/oracle/l2/vOracleL2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IvOracleL2} from "../../interfaces/IvOracleL2.sol";
import {vOracleCommonConstants} from "../vOracleCommon.sol";
abstract contract vOracleL2Constants is vOracleCommonConstants {}
abstract contract vOracleL2Variables is vOracleL2Constants {
AggregatorV3Interface public oracle;
}

abstract contract vOracleL2Errors {
/// @notice Thrown when a zero address is passed to a function where a valid address is required.
error vOracleL2__InvalidZeroAddress();

/// @notice Thrown when the decimals of an oracle do not meet the required specifications.
/// @param expected The expected number of decimals.
/// @param actual The actual number of decimals returned by the oracle.
error vOracleL2__InvalidDecimals(uint8 expected, uint8 actual);

/// @notice Thrown when an oracle price is considered expired due to exceeding the maximum allowable time window.
error vOracleL2__OraclePriceExpired();
}

abstract contract vOracleL2Events {
/// @dev Emitted when an oracle address is updated for a token.
event LogOracleUpdated(address newOracle, address oldOracle);
}

contract vOracleL2 is
IvOracleL2,
Initializable,
OwnableUpgradeable,
UUPSUpgradeable,
vOracleL2Events,
vOracleL2Errors,
vOracleL2Variables
{
using Math for uint256;
/// @dev Prevents implementation contract from being initialized.
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(address owner) public initializer {
__Ownable_init(owner);
}

function _authorizeUpgrade(
address newImplementation
) internal virtual override onlyOwner {}

/// @inheritdoc IvOracleL2
function updateOracle(AggregatorV3Interface _oracle) external onlyOwner {
require(
address(_oracle) != address(0),
vOracleL2__InvalidZeroAddress()
);
// Verify that the pricing of the oracle is less than or equal to 18 decimals - pricing calculations will be off otherwise
require(
_oracle.decimals() <= 18,
vOracleL2__InvalidDecimals(18, _oracle.decimals())
);

emit LogOracleUpdated(address(_oracle), address(oracle));
oracle = _oracle;
}
/// @inheritdoc IvOracleL2
function getMintRate() public view returns (uint256, uint256) {
(, int256 price, , uint256 timestamp, ) = oracle.latestRoundData();
require(
timestamp >= block.timestamp - MAX_TIME_WINDOW,
vOracleL2__OraclePriceExpired()
);
// scale the price to have 18 decimals
uint256 _scaledPrice = (uint256(price)) *
10 ** (18 - oracle.decimals());
return (_scaledPrice, timestamp);
}
}
7 changes: 7 additions & 0 deletions src/oracle/vOracleCommon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

abstract contract vOracleCommonConstants {
/// @dev Specifies the maximum allowable time interval, in seconds, for a price feed to be considered current. Beyond this duration, feeds are considered stale.
uint256 constant MAX_TIME_WINDOW = 86400; // 24 hours
}
6 changes: 3 additions & 3 deletions src/tokens/l1/vETHOFT.sol → src/tokens/l1/vETHOFT_L1.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {Ownable} from "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
Expand All @@ -8,11 +8,11 @@ import {IStakingManager} from "../../interfaces/IStakingManager.sol";

import "forge-std/console2.sol";

abstract contract Constants {
abstract contract vETHOFTConstants {
/// @dev TODO netspec
IStakingManager immutable STAKING_MANAGER;
}
contract vETHOFT is OFT, Constants {
contract vETHOFT_L1 is OFT, vETHOFTConstants {
constructor(
string memory _name,
string memory _symbol,
Expand Down
87 changes: 0 additions & 87 deletions src/tokens/l2/vETHOFT.sol

This file was deleted.

Loading

0 comments on commit 94e72ae

Please sign in to comment.