Skip to content

Commit

Permalink
Add frxETH price feed to OETH Oracle Router (#1779)
Browse files Browse the repository at this point in the history
* Add frxETH price feed to OETH Oracle Router

* Changed asset order in OETHOracleRouter for better gas

* Added OETH Oracle contracts diagram

* Updated OETH Oracle contract diagram

Co-authored-by: Nicholas Addison <nick@addisonbrown.com.au>
  • Loading branch information
shahthepro and naddison36 authored Aug 31, 2023
1 parent 4647604 commit 4c80f7f
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 21 deletions.
38 changes: 19 additions & 19 deletions contracts/contracts/oracle/OracleRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,25 @@ contract OETHOracleRouter is OracleRouter {
override
returns (address feedAddress, uint256 maxStaleness)
{
if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {
if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {
// FIXED_PRICE: WETH/ETH
feedAddress = FIXED_PRICE;
maxStaleness = 0;
} else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {
// frxETH/ETH
feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;
maxStaleness = 18 hours + STALENESS_BUFFER;
} else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth
// Chainlink: stETH/ETH
feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;
maxStaleness = 1 days + STALENESS_BUFFER;
} else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth
// Chainlink: rETH/ETH
feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;
maxStaleness = 1 days + STALENESS_BUFFER;
} else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth
// Chainlink: CRV/ETH
feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;
Expand All @@ -219,29 +237,11 @@ contract OETHOracleRouter is OracleRouter {
// Chainlink: CVX/ETH
feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;
maxStaleness = 1 days + STALENESS_BUFFER;
} else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth
// Chainlink: rETH/ETH
feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;
maxStaleness = 1 days + STALENESS_BUFFER;
} else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth
// Chainlink: cbETH/ETH
feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;
maxStaleness = 1 days + STALENESS_BUFFER;
} else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth
// Chainlink: stETH/ETH
feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;
maxStaleness = 1 days + STALENESS_BUFFER;
} else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {
// FIXED_PRICE: frxETH/ETH
feedAddress = FIXED_PRICE;
maxStaleness = 0;
} else if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {
// FIXED_PRICE: WETH/ETH
feedAddress = FIXED_PRICE;
maxStaleness = 0;
} else {
revert("Asset not available");
}
Expand Down
68 changes: 68 additions & 0 deletions contracts/deploy/074_upgrade_oeth_oracle_router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const addresses = require("../utils/addresses");
const {
deploymentWithGovernanceProposal,
deployWithConfirmation,
withConfirmation,
} = require("../utils/deploy");

module.exports = deploymentWithGovernanceProposal(
{
deployName: "074_upgrade_oeth_oracle_router",
forceDeploy: false,
// forceSkip: true,
reduceQueueTime: true,
deployerIsProposer: true,
},
async () => {
// Current OETH Vault contracts
const cVaultProxy = await ethers.getContract("OETHVaultProxy");
const cVaultAdmin = await ethers.getContractAt(
"VaultAdmin",
cVaultProxy.address
);

// Deploy the new Router
await deployWithConfirmation("OETHOracleRouter");
const cOETHOracleRouter = await ethers.getContract("OETHOracleRouter");

// Cache decimals of all known tokens
await withConfirmation(
// CRV/ETH
cOETHOracleRouter.cacheDecimals(addresses.mainnet.CRV)
);

await withConfirmation(
// CVX/ETH
cOETHOracleRouter.cacheDecimals(addresses.mainnet.CVX)
);

await withConfirmation(
// rETH/ETH
cOETHOracleRouter.cacheDecimals(addresses.mainnet.rETH)
);

await withConfirmation(
// stETH/ETH
cOETHOracleRouter.cacheDecimals(addresses.mainnet.stETH)
);

await withConfirmation(
// frxETH/ETH
cOETHOracleRouter.cacheDecimals(addresses.mainnet.frxETH)
);

// ----------------
// Governance Actions
// ----------------
return {
name: "Add frxETH price feed to OETH Oracle Router",
actions: [
{
contract: cVaultAdmin,
signature: "setPriceProvider(address)",
args: [cOETHOracleRouter.address],
},
],
};
}
);
Binary file added contracts/docs/plantuml/oethOracles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions contracts/docs/plantuml/oethOracles.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
@startuml

skinparam tabSize 2

title "OETH Oracle Contract Dependencies"

object "OETHVault" as vault <<Origin>> #DeepSkyBlue {
assets:
\tWETH
\tfrxETH
\trETH
\tstETH
}

object "OETHOracleRouter" as router <<Origin>> #DeepSkyBlue {
pairs:
\tWETH/ETH
\tfrxETH/ETH
\tstETH/ETH
\trETH/ETH
}

object "FrxEthFraxOracle" as fo <<Frax>> {
pair: frxETH/ETH
}

object "FrxEthEthDualOracle" as fdo <<Frax>> {
pair: frxETH/ETH
}

object "External\nAccess\nControlled\nAggregator" as clrETH <<Chainlink>> {
pair: rETH/ETH
}

object "External\nAccess\nControlled\nAggregator" as clstETH <<Chainlink>> {
pair: stETH/ETH
}

object "External\nAccess\nControlled\nAggregator" as cleth <<Chainlink>> {
pair: ETH/USD
}

object "External\nAccess\nControlled\nAggregator" as clfrax <<Chainlink>> {
pair: FRAX/USD
}

object "frxETH/ETH Pool" as cp <<Curve>> {
assets: frxETH, ETH
}

object "StaticOracle" as uso <<Uniswap>> {
}

object "frxETH/FRAX Pool" as up <<Uniswap>> {
assets: frxETH, FRAX
}


vault ..> router : price(asset)
router ...> clrETH : latestRoundData()
router ...> clstETH : latestRoundData()
router ..> fo : latestRoundData()
fdo .> fo : addRoundData()
fdo ....> cp : price_oracle()
fdo ....> uso : quoteSpecificPoolsWithTimePeriod()
uso .> up : observe()
fdo ..> cleth : latestRoundData()
fdo ..> clfrax : latestRoundData()

@enduml
58 changes: 58 additions & 0 deletions contracts/test/oracle/oracle.fork-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const { expect } = require("chai");
const { parseUnits } = require("ethers/lib/utils");

const { loadDefaultFixture } = require("../_fixture");
const { forkOnlyDescribe, isCI } = require("../helpers");

forkOnlyDescribe("ForkTest: Oracle Routers", function () {
this.timeout(0);

// Retry up to 3 times on CI
this.retries(isCI ? 3 : 0);

let fixture;

describe("OETH Oracle Router", () => {
let oethOracleRouter;
beforeEach(async () => {
fixture = await loadDefaultFixture();
oethOracleRouter = await ethers.getContract("OETHOracleRouter");
});
it("should get rETH price", async () => {
const { reth } = fixture;

const price = await oethOracleRouter.price(reth.address);
expect(price).to.gte(parseUnits("1083", 15));
expect(price).to.lt(parseUnits("109", 16));
});
it("should get frxETH price", async () => {
const { frxETH } = fixture;

const price = await oethOracleRouter.price(frxETH.address);
expect(price).to.lt(parseUnits("1", 18));
});
it("should get WETH price", async () => {
const { weth } = fixture;

const price = await oethOracleRouter.price(weth.address);
expect(price).to.eq(parseUnits("1", 18));
});
it("should get stETH price", async () => {
const { stETH } = fixture;

const price = await oethOracleRouter.price(stETH.address);
expect(price).to.approxEqualTolerance(parseUnits("1", 18), 1);
expect(price).to.not.eq(parseUnits("1", 18));
});
it("should get gas costs of assets", async () => {
const { reth, frxETH, stETH, weth, josh } = fixture;

for (const asset of [frxETH, reth, stETH, weth]) {
const tx = await oethOracleRouter
.connect(josh)
.populateTransaction.price(asset.address);
await josh.sendTransaction(tx);
}
});
});
});
7 changes: 5 additions & 2 deletions contracts/test/vault/oeth-vault.fork-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { expect } = require("chai");
const { parseUnits } = require("ethers/lib/utils");
const { formatUnits, parseUnits } = require("ethers/lib/utils");

const addresses = require("../../utils/addresses");
const {
Expand All @@ -9,6 +9,8 @@ const {
} = require("../_fixture");
const { forkOnlyDescribe, isCI } = require("../helpers");

const log = require("../../utils/logger")("test:fork:oeth:vault");

const oethWhaleAddress = "0xEADB3840596cabF312F2bC88A4Bb0b93A4E1FF5F";

forkOnlyDescribe("ForkTest: OETH Vault", function () {
Expand Down Expand Up @@ -76,7 +78,7 @@ forkOnlyDescribe("ForkTest: OETH Vault", function () {
.connect(josh)
.mint(asset.address, amount, minOeth);

if (asset === weth || asset === frxETH) {
if (asset === weth) {
await expect(tx)
.to.emit(oethVault, "Mint")
.withArgs(josh.address, amount);
Expand Down Expand Up @@ -120,6 +122,7 @@ forkOnlyDescribe("ForkTest: OETH Vault", function () {
const { oeth, oethVault, timelock } = fixture;

const oethWhaleBalance = await oeth.balanceOf(oethWhaleAddress);
log(`OETH whale balance: ${formatUnits(oethWhaleBalance)}`);
expect(oethWhaleBalance, "no longer an OETH whale").to.gt(
parseUnits("1000", 18)
);
Expand Down

0 comments on commit 4c80f7f

Please sign in to comment.