Skip to content

Add deployment file for Bridged wOETH Strategy #2496

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
83 changes: 83 additions & 0 deletions contracts/contracts/oracle/OETHPlumeOracleRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { AbstractOracleRouter } from "./AbstractOracleRouter.sol";
import { StableMath } from "../utils/StableMath.sol";
import "../interfaces/chainlink/AggregatorV3Interface.sol";

// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH
contract OETHPlumeOracleRouter is AbstractOracleRouter {
using StableMath for uint256;
using SafeCast for int256;

address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;
address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;
// Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume
address constant WOETH_ORACLE_FEED =
0x4915600Ed7d85De62011433eEf0BD5399f677e9b;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a trustworthy feed? How high is our confidence in this one?


constructor() {}

/**
* @notice Returns the total price in 18 digit units for a given asset.
* This implementation does not (!) do range checks as the
* parent OracleRouter does.
* @param asset address of the asset
* @return uint256 unit price for 1 asset unit, in 18 decimal fixed
*/
function price(address asset)
external
view
virtual
override
returns (uint256)
{
(address _feed, uint256 maxStaleness) = feedMetadata(asset);
if (_feed == FIXED_PRICE) {
return 1e18;

Check warning on line 38 in contracts/contracts/oracle/OETHPlumeOracleRouter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/oracle/OETHPlumeOracleRouter.sol#L38

Added line #L38 was not covered by tests
}
require(_feed != address(0), "Asset not available");

// slither-disable-next-line unused-return
(, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)
.latestRoundData();

require(
updatedAt + maxStaleness >= block.timestamp,
"Oracle price too old"
);

uint8 decimals = getDecimals(_feed);
uint256 _price = _iprice.toUint256().scaleBy(18, decimals);
return _price;
}

/**
* @dev The price feed contract to use for a particular asset along with
* maximum data staleness
* @param asset address of the asset
* @return feedAddress address of the price feed for the asset
* @return maxStaleness maximum acceptable data staleness duration
*/
function feedMetadata(address asset)
internal
view
virtual
override
returns (address feedAddress, uint256 maxStaleness)
{
if (asset == WETH) {
// FIXED_PRICE: WETH/ETH
feedAddress = FIXED_PRICE;
maxStaleness = 0;

Check warning on line 73 in contracts/contracts/oracle/OETHPlumeOracleRouter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/oracle/OETHPlumeOracleRouter.sol#L72-L73

Added lines #L72 - L73 were not covered by tests
} else if (asset == WOETH) {
// Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this should be from plume chain, but not a big deal.

// Bridged wOETH/OETH
feedAddress = WOETH_ORACLE_FEED;
maxStaleness = 1 days + STALENESS_BUFFER;
} else {
revert("Asset not available");

Check warning on line 80 in contracts/contracts/oracle/OETHPlumeOracleRouter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/oracle/OETHPlumeOracleRouter.sol#L80

Added line #L80 was not covered by tests
}
}
}
104 changes: 104 additions & 0 deletions contracts/deploy/plume/003_woeth_strategy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const { deployOnPlume } = require("../../utils/deploy-l2");
const {
deployWithConfirmation,
withConfirmation,
} = require("../../utils/deploy");
const addresses = require("../../utils/addresses");

module.exports = deployOnPlume(
{
deployName: "003_woeth_strategy",
},
async ({ ethers }) => {
const { deployerAddr, timelockAddr } = await getNamedAccounts();

const sDeployer = await ethers.getSigner(deployerAddr);

const cOETHpVaultProxy = await ethers.getContract("OETHPlumeVaultProxy");
const cOETHpProxy = await ethers.getContract("OETHPlumeProxy");
const cOETHpVault = await ethers.getContractAt(
"IVault",
cOETHpVaultProxy.address
);

// Deploy oracle router
await deployWithConfirmation("OETHPlumeOracleRouter");
const cOracleRouter = await ethers.getContract("OETHPlumeOracleRouter");

// Cache decimals
await withConfirmation(
cOracleRouter.cacheDecimals(addresses.plume.BridgedWOETH)
);

// Deploy proxy
await deployWithConfirmation("BridgedWOETHStrategyProxy");
const cStrategyProxy = await ethers.getContract(
"BridgedWOETHStrategyProxy"
);

// Deploy implementation
const dStrategyImpl = await deployWithConfirmation("BridgedWOETHStrategy", [
[addresses.zero, cOETHpVaultProxy.address],
addresses.plume.WETH,
addresses.plume.BridgedWOETH,
cOETHpProxy.address,
]);
const cStrategy = await ethers.getContractAt(
"BridgedWOETHStrategy",
cStrategyProxy.address
);

// Init Strategy
const initData = cStrategy.interface.encodeFunctionData(
"initialize(uint128)",
[
100, // 1% maxPriceDiffBps
]
);
// prettier-ignore
await withConfirmation(
cStrategyProxy
.connect(sDeployer)["initialize(address,address,bytes)"](
dStrategyImpl.address,
timelockAddr,
initData
)
);
console.log("Initialized BridgedWOETHStrategyProxy");

return {
actions: [
{
// 1. Update oracle router
contract: cOETHpVault,
signature: "setPriceProvider(address)",
args: [cOracleRouter.address],
},
{
// 2. Approve strategy
contract: cOETHpVault,
signature: "approveStrategy(address)",
args: [cStrategyProxy.address],
},
{
// 3. Add to mint whitelist
contract: cOETHpVault,
signature: "addStrategyToMintWhitelist(address)",
args: [cStrategyProxy.address],
},
{
// 4. Update oracle price
contract: cStrategy,
signature: "updateWOETHOraclePrice()",
args: [],
},
{
// 5. Set strategist as Harvester
contract: cStrategy,
signature: "setHarvesterAddress(address)",
args: [addresses.plume.strategist],
},
],
};
}
);
42 changes: 42 additions & 0 deletions contracts/test/_fixture-plume.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const addresses = require("../utils/addresses");
const hhHelpers = require("@nomicfoundation/hardhat-network-helpers");
const log = require("../utils/logger")("test:fixtures-plume");

const MINTER_ROLE =
"0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6";
const BURNER_ROLE =
"0x3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848";

let snapshotId;
const defaultPlumeFixture = deployments.createFixture(async () => {
if (!snapshotId && !isFork) {
Expand Down Expand Up @@ -75,6 +80,33 @@ const defaultPlumeFixture = deployments.createFixture(async () => {
oethpVaultProxy.address
);

// Bridged wOETH
const woethProxy = await ethers.getContract("BridgedWOETHProxy");
const woeth = await ethers.getContractAt("BridgedWOETH", woethProxy.address);

let woethStrategy;

if (isFork) {
const woethStrategyProxy = await ethers.getContract(
"BridgedWOETHStrategyProxy"
);
woethStrategy = await ethers.getContractAt(
"BridgedWOETHStrategy",
woethStrategyProxy.address
);

// Make sure we can print bridged WOETH for tests
await woeth.connect(governor).grantRole(MINTER_ROLE, governor.address);
await woeth.connect(governor).grantRole(BURNER_ROLE, governor.address);

// Mint some bridged WOETH
for (const user of [governor, rafael, nick, clement]) {
// Mint some bridged WOETH
await woeth.connect(governor).mint(user.address, oethUnits("1"));
}
}

// And we can mint WETH
const wethMintableContract = await ethers.getContractAt(
[
"function addMinter(address) external",
Expand All @@ -84,6 +116,10 @@ const defaultPlumeFixture = deployments.createFixture(async () => {
addresses.plume.WETH
);

const oracleRouter = await ethers.getContract(
isFork ? "OETHPlumeOracleRouter" : "MockOracleRouter"
);

const _mintWETH = async (signer, amount) => {
if (isFork) {
await wethMintableContract.connect(governor).mint(signer.address, amount);
Expand Down Expand Up @@ -132,6 +168,12 @@ const defaultPlumeFixture = deployments.createFixture(async () => {
wOETHp,
oethpVault,

// Bridged wOETH
woeth,
woethProxy,
woethStrategy,
oracleRouter,

// Helpers
_mintWETH,
};
Expand Down
Loading
Loading