From 58e01aa0b8d37d97d61f1c57de854f5844b6fc03 Mon Sep 17 00:00:00 2001 From: Narayan Prusty Date: Tue, 27 Feb 2024 22:09:50 +0530 Subject: [PATCH] fix: handle frxETH depeg scenario --- contracts/oracles/EtherFiOracle.sol | 4 +- contracts/oracles/FraxOracle.sol | 23 +++++++++++- helpers/deploymentConfig.ts | 1 + test/EtherFiOracle.ts | 5 ++- test/FraxOracle.ts | 57 ++++++++++++++++++++++++----- 5 files changed, 76 insertions(+), 14 deletions(-) diff --git a/contracts/oracles/EtherFiOracle.sol b/contracts/oracles/EtherFiOracle.sol index 6a66e762..18909763 100644 --- a/contracts/oracles/EtherFiOracle.sol +++ b/contracts/oracles/EtherFiOracle.sol @@ -56,7 +56,9 @@ contract EtherFiOracle is OracleInterface { uint256 eETHAmount = asset == address(eETH) ? EXP_SCALE : weETH.getEETHByWeETH(EXP_SCALE); // Calculate price of 1 eETH scaled by 1e18 - uint256 eETHUSDPrice = RESILIENT_ORACLE.getPrice(ASSUME_EETH_ETH_EQUIVALENCE ? NATIVE_TOKEN_ADDR : address(eETH)); + uint256 eETHUSDPrice = RESILIENT_ORACLE.getPrice( + ASSUME_EETH_ETH_EQUIVALENCE ? NATIVE_TOKEN_ADDR : address(eETH) + ); // eETH amount (for 1 weETH or eETH) * usdPrice (of eETH) / 1e18 return (eETHAmount * eETHUSDPrice) / EXP_SCALE; diff --git a/contracts/oracles/FraxOracle.sol b/contracts/oracles/FraxOracle.sol index f588a3a0..b15404eb 100644 --- a/contracts/oracles/FraxOracle.sol +++ b/contracts/oracles/FraxOracle.sol @@ -13,6 +13,10 @@ import { EXP_SCALE } from "@venusprotocol/solidity-utilities/contracts/constants * @notice This oracle fetches the price of sFrax and sfrxETH assets */ contract FraxOracle is OracleInterface { + /// @notice A flag assuming 1:1 price equivalence between frxETH/ETH + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + bool public immutable ASSUME_FRXETH_ETH_EQUIVALENCE; + /// @notice Address of sfraxETH /// @custom:oz-upgrades-unsafe-allow state-variable-immutable ISfraxETH public immutable sfraxETH; @@ -25,6 +29,10 @@ contract FraxOracle is OracleInterface { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable ISFrax public immutable sFRAX; + /// @notice Address of fraxETH + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address public immutable fraxETH; + /// @notice Address of Resilient Oracle /// @custom:oz-upgrades-unsafe-allow state-variable-immutable OracleInterface public immutable RESILIENT_ORACLE; @@ -34,17 +42,28 @@ contract FraxOracle is OracleInterface { /// @notice Constructor for the implementation contract. /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address _frax, address _sFrax, address _eth, address _sfraxETH, address _resilientOracleAddress) { + constructor( + address _frax, + address _sFrax, + address _eth, + address _sfraxETH, + address _fraxETH, + address _resilientOracleAddress, + bool _assumeEquivalence + ) { ensureNonzeroAddress(_frax); ensureNonzeroAddress(_sFrax); ensureNonzeroAddress(_eth); ensureNonzeroAddress(_sfraxETH); + ensureNonzeroAddress(_fraxETH); ensureNonzeroAddress(_resilientOracleAddress); FRAX = _frax; sFRAX = ISFrax(_sFrax); ETH = _eth; sfraxETH = ISfraxETH(_sfraxETH); + fraxETH = _fraxETH; RESILIENT_ORACLE = OracleInterface(_resilientOracleAddress); + ASSUME_FRXETH_ETH_EQUIVALENCE = _assumeEquivalence; } /** @@ -59,7 +78,7 @@ contract FraxOracle is OracleInterface { if (asset == address(sFRAX)) { assetPriceInUSD = RESILIENT_ORACLE.getPrice(FRAX); } else { - assetPriceInUSD = RESILIENT_ORACLE.getPrice(ETH); + assetPriceInUSD = RESILIENT_ORACLE.getPrice(ASSUME_FRXETH_ETH_EQUIVALENCE ? ETH : address(fraxETH)); } // get FRAX or ETH amount for 1 sFrax or sfraxETH scaled by 1e18 diff --git a/helpers/deploymentConfig.ts b/helpers/deploymentConfig.ts index fe1183c8..80bf2ad3 100644 --- a/helpers/deploymentConfig.ts +++ b/helpers/deploymentConfig.ts @@ -115,6 +115,7 @@ export const ADDRESSES: PreconfiguredAddresses = { sFRAX: "0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32", sfraxETH: "0xac3e018457b222d93114458476f3e3416abbe38f", FRAX: "0x853d955aCEf822Db058eb8505911ED77F175b99e", + fraxETH: "0x5e8422345238f34275888049021821e8e08caa1f", weETH: "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", eETH: "0x35fA164735182de50811E8e2E824cFb9B6118ac2", }, diff --git a/test/EtherFiOracle.ts b/test/EtherFiOracle.ts index 87566efa..4c25c27a 100644 --- a/test/EtherFiOracle.ts +++ b/test/EtherFiOracle.ts @@ -37,7 +37,8 @@ describe("EtherFiOracle unit tests", () => { describe("deployment", () => { it("revert if weETH address is 0", async () => { - await expect(EtherFiOracleFactory.deploy(addr0000, eETHMock.address, resilientOracleMock.address, true)).to.be.reverted; + await expect(EtherFiOracleFactory.deploy(addr0000, eETHMock.address, resilientOracleMock.address, true)).to.be + .reverted; }); it("revert if eETH address is 0", async () => { await expect(EtherFiOracleFactory.deploy(weETHMock.address, addr0000, resilientOracleMock.address, true)).to.be @@ -51,7 +52,7 @@ describe("EtherFiOracle unit tests", () => { weETHMock.address, eETHMock.address, resilientOracleMock.address, - true + true, ); }); }); diff --git a/test/FraxOracle.ts b/test/FraxOracle.ts index 332355b1..67bbaebd 100644 --- a/test/FraxOracle.ts +++ b/test/FraxOracle.ts @@ -10,7 +10,7 @@ import { addr0000 } from "./utils/data"; const { expect } = chai; chai.use(smock.matchers); -const { FRAX, sFRAX, sfraxETH } = ADDRESSES.ethereum; +const { FRAX, sFRAX, sfraxETH, fraxETH } = ADDRESSES.ethereum; const WETH = assets.ethereum.find(asset => asset.token === "WETH")?.address; const FRAX_USD_PRICE = parseUnits("0.9979", 18); // 0.99 USD for 1 FRAX const ETH_USD_PRICE = parseUnits("3100", 18); // 3100 USD for 1 ETH @@ -33,28 +33,65 @@ describe("FraxOracle unit tests", () => { sFraxETHMock.convertToAssets.returns(parseUnits("1.076546447254363344", 18)); FraxOracleFactory = await ethers.getContractFactory("FraxOracle"); - - console.log(FRAX, sFRAX, WETH, sfraxETH); }); describe("deployment", () => { it("revert if FRAX address is 0", async () => { await expect( - FraxOracleFactory.deploy(addr0000, sFraxMock.address, WETH, sFraxETHMock.address, resilientOracleMock.address), + FraxOracleFactory.deploy( + addr0000, + sFraxMock.address, + WETH, + sFraxETHMock.address, + fraxETH, + resilientOracleMock.address, + true, + ), ).to.be.reverted; }); it("revert if sFRAX address is 0", async () => { - await expect(FraxOracleFactory.deploy(FRAX, addr0000, WETH, sFraxETHMock.address, resilientOracleMock.address)).to - .be.reverted; + await expect( + FraxOracleFactory.deploy( + FRAX, + addr0000, + WETH, + sFraxETHMock.address, + fraxETH, + resilientOracleMock.address, + true, + ), + ).to.be.reverted; }); it("revert if ETH address is 0", async () => { await expect( - FraxOracleFactory.deploy(FRAX, sFraxMock.address, addr0000, sFraxETHMock.address, resilientOracleMock.address), + FraxOracleFactory.deploy( + FRAX, + sFraxMock.address, + addr0000, + sFraxETHMock.address, + fraxETH, + resilientOracleMock.address, + true, + ), ).to.be.reverted; }); it("revert if sfraxETH address is 0", async () => { - await expect(FraxOracleFactory.deploy(FRAX, sFraxMock.address, WETH, addr0000, resilientOracleMock.address)).to.be - .reverted; + await expect( + FraxOracleFactory.deploy(FRAX, sFraxMock.address, WETH, addr0000, fraxETH, resilientOracleMock.address, true), + ).to.be.reverted; + }); + it("revert if sfraxETH address is 0", async () => { + await expect( + FraxOracleFactory.deploy( + FRAX, + sFraxMock.address, + WETH, + sFraxETHMock.address, + addr0000, + resilientOracleMock.address, + true, + ), + ).to.be.reverted; }); it("should deploy contract", async () => { FraxOracle = await FraxOracleFactory.deploy( @@ -62,7 +99,9 @@ describe("FraxOracle unit tests", () => { sFraxMock.address, WETH, sFraxETHMock.address, + fraxETH, resilientOracleMock.address, + true, ); }); });