generated from PaulRBerg/hardhat-template
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #191 from VenusProtocol/feat/sfrxeth-oracle
[VEN-2551] Get sfrxETH Price from SfrxEthFraxOracle
- Loading branch information
Showing
22 changed files
with
5,865 additions
and
46 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity 0.8.25; | ||
|
||
interface ISfrxEthFraxOracle { | ||
function getPrices() external view returns (bool _isbadData, uint256 _priceLow, uint256 _priceHigh); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,108 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity 0.8.25; | ||
|
||
import { ISfrxETH } from "../interfaces/ISfrxETH.sol"; | ||
import { CorrelatedTokenOracle } from "./common/CorrelatedTokenOracle.sol"; | ||
import { ISfrxEthFraxOracle } from "../interfaces/ISfrxEthFraxOracle.sol"; | ||
import { ensureNonzeroAddress, ensureNonzeroValue } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; | ||
import { EXP_SCALE } from "@venusprotocol/solidity-utilities/contracts/constants.sol"; | ||
import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol"; | ||
import { OracleInterface } from "../interfaces/OracleInterface.sol"; | ||
|
||
/** | ||
* @title SFrxETHOracle | ||
* @author Venus | ||
* @notice This oracle fetches the price of sfrxETH | ||
*/ | ||
contract SFrxETHOracle is CorrelatedTokenOracle { | ||
contract SFrxETHOracle is AccessControlledV8, OracleInterface { | ||
/// @notice Address of SfrxEthFraxOracle | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
ISfrxEthFraxOracle public immutable SFRXETH_FRAX_ORACLE; | ||
|
||
/// @notice Address of sfrxETH | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
address public immutable SFRXETH; | ||
|
||
/// @notice Maximum allowed price difference | ||
uint256 public maxAllowedPriceDifference; | ||
|
||
/// @notice Emits when the maximum allowed price difference is updated | ||
event MaxAllowedPriceDifferenceUpdated(uint256 oldMaxAllowedPriceDifference, uint256 newMaxAllowedPriceDifference); | ||
|
||
/// @notice Thrown if the price data is invalid | ||
error BadPriceData(); | ||
|
||
/// @notice Thrown if the price difference exceeds the allowed limit | ||
error PriceDifferenceExceeded(); | ||
|
||
/// @notice Thrown if the token address is invalid | ||
error InvalidTokenAddress(); | ||
|
||
/// @notice Constructor for the implementation contract. | ||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor( | ||
address sfrxETH, | ||
address frxETH, | ||
address resilientOracle | ||
) CorrelatedTokenOracle(sfrxETH, frxETH, resilientOracle) {} | ||
/// @custom:error ZeroAddressNotAllowed is thrown when `_sfrxEthFraxOracle` or `_sfrxETH` are the zero address | ||
constructor(address _sfrxEthFraxOracle, address _sfrxETH) { | ||
ensureNonzeroAddress(_sfrxEthFraxOracle); | ||
ensureNonzeroAddress(_sfrxETH); | ||
|
||
SFRXETH_FRAX_ORACLE = ISfrxEthFraxOracle(_sfrxEthFraxOracle); | ||
SFRXETH = _sfrxETH; | ||
|
||
_disableInitializers(); | ||
} | ||
|
||
/** | ||
* @notice Sets the contracts required to fetch prices | ||
* @param _accessControlManager Address of the access control manager contract | ||
* @param _maxAllowedPriceDifference Maximum allowed price difference | ||
* @custom:error ZeroValueNotAllowed is thrown if `_maxAllowedPriceDifference` is zero | ||
*/ | ||
function initialize(address _accessControlManager, uint256 _maxAllowedPriceDifference) external initializer { | ||
ensureNonzeroValue(_maxAllowedPriceDifference); | ||
|
||
__AccessControlled_init(_accessControlManager); | ||
maxAllowedPriceDifference = _maxAllowedPriceDifference; | ||
} | ||
|
||
/** | ||
* @notice Sets the maximum allowed price difference | ||
* @param _maxAllowedPriceDifference Maximum allowed price difference | ||
* @custom:error ZeroValueNotAllowed is thrown if `_maxAllowedPriceDifference` is zero | ||
*/ | ||
function setMaxAllowedPriceDifference(uint256 _maxAllowedPriceDifference) external { | ||
_checkAccessAllowed("setMaxAllowedPriceDifference(uint256)"); | ||
ensureNonzeroValue(_maxAllowedPriceDifference); | ||
|
||
emit MaxAllowedPriceDifferenceUpdated(maxAllowedPriceDifference, _maxAllowedPriceDifference); | ||
maxAllowedPriceDifference = _maxAllowedPriceDifference; | ||
} | ||
|
||
/** | ||
* @notice Gets the frxETH for 1 sfrxETH | ||
* @return amount Amount of frxETH | ||
* @notice Fetches the USD price of sfrxETH | ||
* @param asset Address of the sfrxETH token | ||
* @return price The price scaled by 1e18 | ||
* @custom:error InvalidTokenAddress is thrown when the `asset` is not the sfrxETH token (`SFRXETH`) | ||
* @custom:error BadPriceData is thrown if the `SFRXETH_FRAX_ORACLE` oracle informs it has bad data | ||
* @custom:error ZeroValueNotAllowed is thrown if the prices (low or high, in USD) are zero | ||
* @custom:error PriceDifferenceExceeded is thrown if priceHigh/priceLow is greater than `maxAllowedPriceDifference` | ||
*/ | ||
function _getUnderlyingAmount() internal view override returns (uint256) { | ||
return ISfrxETH(CORRELATED_TOKEN).convertToAssets(EXP_SCALE); | ||
function getPrice(address asset) external view returns (uint256) { | ||
if (asset != SFRXETH) revert InvalidTokenAddress(); | ||
|
||
(bool isBadData, uint256 priceLow, uint256 priceHigh) = SFRXETH_FRAX_ORACLE.getPrices(); | ||
|
||
if (isBadData) revert BadPriceData(); | ||
|
||
// calculate price in USD | ||
uint256 priceHighInUSD = (EXP_SCALE ** 2) / priceLow; | ||
uint256 priceLowInUSD = (EXP_SCALE ** 2) / priceHigh; | ||
|
||
ensureNonzeroValue(priceHighInUSD); | ||
ensureNonzeroValue(priceLowInUSD); | ||
|
||
// validate price difference | ||
uint256 difference = (priceHighInUSD * EXP_SCALE) / priceLowInUSD; | ||
if (difference > maxAllowedPriceDifference) revert PriceDifferenceExceeded(); | ||
|
||
// calculate and return average price | ||
return (priceHighInUSD + priceLowInUSD) / 2; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity 0.8.25; | ||
|
||
import "../../interfaces/ISfrxEthFraxOracle.sol"; | ||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
contract MockSfrxEthFraxOracle is ISfrxEthFraxOracle, Ownable { | ||
bool public isBadData; | ||
uint256 public priceLow; | ||
uint256 public priceHigh; | ||
|
||
constructor() Ownable() {} | ||
|
||
function setPrices(bool _isBadData, uint256 _priceLow, uint256 _priceHigh) external onlyOwner { | ||
isBadData = _isBadData; | ||
priceLow = _priceLow; | ||
priceHigh = _priceHigh; | ||
} | ||
|
||
function getPrices() external view override returns (bool, uint256, uint256) { | ||
return (isBadData, priceLow, priceHigh); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { parseUnits } from "ethers/lib/utils"; | ||
import { ethers } from "hardhat"; | ||
import { DeployFunction } from "hardhat-deploy/dist/types"; | ||
import { HardhatRuntimeEnvironment } from "hardhat/types"; | ||
|
||
import { ADDRESSES } from "../helpers/deploymentConfig"; | ||
|
||
const func: DeployFunction = async ({ getNamedAccounts, deployments, network }: HardhatRuntimeEnvironment) => { | ||
const { deploy } = deployments; | ||
const { deployer } = await getNamedAccounts(); | ||
|
||
const proxyOwnerAddress = network.live ? ADDRESSES[network.name].timelock : deployer; | ||
|
||
const { sfrxETH, SfrxEthFraxOracle, acm } = ADDRESSES[network.name]; | ||
const maxAllowedPriceDifference = parseUnits("1.14", 18); | ||
|
||
let SfrxEthFraxOracleAddress = SfrxEthFraxOracle; | ||
if (!SfrxEthFraxOracle) { | ||
await deploy("MockSfrxEthFraxOracle", { | ||
contract: "MockSfrxEthFraxOracle", | ||
from: deployer, | ||
log: true, | ||
autoMine: true, | ||
skipIfAlreadyDeployed: true, | ||
args: [], | ||
}); | ||
|
||
const mockSfrxEthFraxOracle = await ethers.getContract("MockSfrxEthFraxOracle"); | ||
SfrxEthFraxOracleAddress = mockSfrxEthFraxOracle.address; | ||
|
||
if ((await mockSfrxEthFraxOracle.owner()) === deployer) { | ||
await mockSfrxEthFraxOracle.transferOwnership(proxyOwnerAddress); | ||
} | ||
} | ||
|
||
await deploy("SFrxETHOracle", { | ||
contract: "SFrxETHOracle", | ||
from: deployer, | ||
log: true, | ||
deterministicDeployment: false, | ||
args: [SfrxEthFraxOracleAddress, sfrxETH], | ||
proxy: { | ||
owner: proxyOwnerAddress, | ||
proxyContract: "OptimizedTransparentProxy", | ||
execute: { | ||
methodName: "initialize", | ||
args: [acm, maxAllowedPriceDifference], | ||
}, | ||
}, | ||
skipIfAlreadyDeployed: true, | ||
}); | ||
|
||
const sfrxETHOracle = await ethers.getContract("SFrxETHOracle"); | ||
|
||
if ((await sfrxETHOracle.owner()) === deployer) { | ||
await sfrxETHOracle.transferOwnership(proxyOwnerAddress); | ||
} | ||
}; | ||
|
||
export default func; | ||
func.tags = ["sFraxETHOracle"]; | ||
func.skip = async (hre: HardhatRuntimeEnvironment) => hre.network.name !== "ethereum" && hre.network.name !== "sepolia"; |
Oops, something went wrong.