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

[VEN-2551] Get sfrxETH Price from SfrxEthFraxOracle #191

Merged
merged 28 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fc52a39
fix: use sfrxEthFraxOracle oracle
web3rover May 8, 2024
6f47ec2
fix: removed ISfrxETH
web3rover May 8, 2024
0206218
fix: fixed tests
web3rover May 8, 2024
2a10937
fix: added acm
web3rover May 8, 2024
526eacf
fix: added price difference check
web3rover May 8, 2024
f475e6b
fix: remove correlated token oracle
web3rover May 8, 2024
95e0494
fix: make sfrxETH immutable
web3rover May 8, 2024
a46c67d
fix: changed order
web3rover May 8, 2024
3058ef1
fix: fix comment
web3rover May 9, 2024
fc62a15
fix: fix comment
web3rover May 9, 2024
ae84726
fix: update var name
web3rover May 9, 2024
5512927
fix: fixed vars
web3rover May 9, 2024
54462b8
fix: added netspec
web3rover May 9, 2024
6130942
fix: sfe-05
web3rover May 16, 2024
b24ef72
fix: sfe-03
web3rover May 16, 2024
0cd68b2
fix: ven-1
web3rover May 24, 2024
6374a84
fix: ven-2
web3rover May 24, 2024
5367980
Merge branch 'develop' of github.com:VenusProtocol/oracle into feat/s…
web3rover May 28, 2024
f76cf1e
feat: use ratio for price difference
web3rover May 28, 2024
c5ae0fa
fix: deployed oracle
web3rover Jun 10, 2024
41bac60
feat: updating deployment files
Narayanprusty Jun 10, 2024
0b221a7
docs: add audit reports for the sfrxETH oracle
chechu Jun 18, 2024
5b3b6ed
fix: redeployed oracle on testnet
web3rover Jun 18, 2024
25ab625
fix: deployed on ethereum
web3rover Jun 18, 2024
3991356
fix: revert config
web3rover Jun 18, 2024
5d43253
Merge pull request #202 from VenusProtocol/feat/deploy-sfrxeth-oracle
web3rover Jun 19, 2024
28900dd
feat: updating deployment files
Narayanprusty Jun 19, 2024
36f63b5
docs: add custom error natspec comments for SfrxETHOracle
chechu Jun 20, 2024
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
Binary file added audits/110_sfrxETHOracle_certik_20240517.pdf
Binary file not shown.
Binary file added audits/111_sfrxETHOracle_quantstamp_20240530.pdf
Binary file not shown.
7 changes: 0 additions & 7 deletions contracts/interfaces/ISfrxETH.sol

This file was deleted.

6 changes: 6 additions & 0 deletions contracts/interfaces/ISfrxEthFraxOracle.sol
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);
}
103 changes: 91 additions & 12 deletions contracts/oracles/SFrxETHOracle.sol
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 {
chechu marked this conversation as resolved.
Show resolved Hide resolved
_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
Debugger022 marked this conversation as resolved.
Show resolved Hide resolved
* @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();
chechu marked this conversation as resolved.
Show resolved Hide resolved

// 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;
chechu marked this conversation as resolved.
Show resolved Hide resolved
}
}
23 changes: 23 additions & 0 deletions contracts/oracles/mocks/MockSFrxEthFraxOracle.sol
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);
}
}
62 changes: 62 additions & 0 deletions deploy/8-deploy-sfrxeth-oracle.ts
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";
Loading
Loading