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

Oeth L2 base plugin #1226

Open
wants to merge 22 commits into
base: 4.0.0
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ jobs:
restore-keys: |
hardhat-network-fork-${{ runner.os }}-
hardhat-network-fork-
- run: yarn hardhat test ./test/plugins/individual-collateral/{cbeth,aave-v3,compoundv3,stargate,lido,meta-morpho,aerodrome}/*.test.ts
- run: yarn hardhat test ./test/plugins/individual-collateral/{cbeth,aave-v3,compoundv3,stargate,lido,meta-morpho,aerodrome,origin}/*.test.ts
env:
NODE_OPTIONS: '--max-old-space-size=32768'
TS_NODE_SKIP_IGNORE: true
Expand Down
3 changes: 3 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface ITokens {
wsgUSDbC?: string
yvCurveUSDPcrvUSD?: string
yvCurveUSDCcrvUSD?: string
wsuperOETHb?: string

pyUSD?: string
aEthPyUSD?: string
Expand Down Expand Up @@ -536,6 +537,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
USDz: '0x04D5ddf5f3a8939889F11E97f8c4BB48317F1938',
meUSD: '0xbb819D845b573B5D7C538F5b85057160cfb5f313',
AERO: '0x940181a94A35A4569E4529A3CDfB74e38FD98631',
wsuperOETHb: '0x7FcD174E80f264448ebeE8c88a7C4476AAF58Ea6',
},
chainlinkFeeds: {
DAI: '0x591e79239a7d679378ec8c847e5038150364c78f', // 0.3%, 24hr
Expand All @@ -555,6 +557,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
eUSD: '0x9b2C948dbA5952A1f5Ab6fA16101c1392b8da1ab', // 0.5%, 24h
USDz: '0xe25969e2Fa633a0C027fAB8F30Fc9C6A90D60B48', // 0.5%, 24h
AERO: '0x4EC5970fC728C5f65ba413992CD5fF6FD70fcfF0', // 0.5%, 24h
wsuperOETHb: '0x28C964c985fe84736fAdc7Cf0bBd58B54bc7CF93',
},
GNOSIS_EASY_AUCTION: '0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02', // mock
COMET_REWARDS: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1',
Expand Down
87 changes: 87 additions & 0 deletions contracts/plugins/assets/origin/OETHCollateralL2Base.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "../../../libraries/Fixed.sol";
import "../ERC4626FiatCollateral.sol";
import "../OracleLib.sol";

interface IMorphoChainlinkOracleV2 {
function price() external view returns (uint256);
}

/**
* @title Origin Staked ETH Collateral for Base L2
* @notice Collateral plugin for Origin OETH,
* tok = wsuperOETHb (wrapped superOETHb)
* ref = superOETHb (pegged to ETH 1:1)
* tar = ETH
* UoA = USD
*/
contract OETHCollateralL2Base is ERC4626FiatCollateral {
using OracleLib for AggregatorV3Interface;
using FixLib for uint192;

IMorphoChainlinkOracleV2 public immutable targetPerTokChainlinkFeed; // {tar/token}

AggregatorV3Interface public immutable uoaPerTargetChainlinkFeed; // {UoA/tar}
julianmrodri marked this conversation as resolved.
Show resolved Hide resolved
uint48 public immutable uoaPerTargetChainlinkTimeout; // {s}

/// @param config.chainlinkFeed - ignored
/// @param config.oracleTimeout - ignored
constructor(
CollateralConfig memory config,
uint192 revenueHiding,
IMorphoChainlinkOracleV2 _targetPerTokChainlinkFeed,
AggregatorV3Interface _uoaPerTargetChainlinkFeed,
uint48 _uoaPerTargetChainlinkTimeout
) ERC4626FiatCollateral(config, revenueHiding) {
require(config.defaultThreshold != 0, "defaultThreshold zero");

require(address(_targetPerTokChainlinkFeed) != address(0), "targetPerTokFeed missing");
require(address(_uoaPerTargetChainlinkFeed) != address(0), "uoaPerTargetFeed missing");

targetPerTokChainlinkFeed = _targetPerTokChainlinkFeed;

uoaPerTargetChainlinkFeed = _uoaPerTargetChainlinkFeed;
uoaPerTargetChainlinkTimeout = _uoaPerTargetChainlinkTimeout;

maxOracleTimeout = uint48(Math.max(maxOracleTimeout, _uoaPerTargetChainlinkTimeout));
}

/// Can revert, used by other contract functions in order to catch errors
/// @return low {UoA/tok} The low price estimate
/// @return high {UoA/tok} The high price estimate
/// @return pegPrice {target/ref} The actual price observed in the peg
function tryPrice()
external
view
override
returns (
uint192 low,
uint192 high,
uint192 pegPrice
)
{
// {tar/tok}
// {ETH/wsuperOETHb}
uint192 targetPerTok = _safeWrap(targetPerTokChainlinkFeed.price() / FIX_ONE);

// {UoA/tar}
// {USD/ETH}
uint192 uoaPerTar = uoaPerTargetChainlinkFeed.price(uoaPerTargetChainlinkTimeout);

// {UoA/tok} = {UoA/tar} * {tar/tok}
// USD/wsuperOETHb = USD/ETH * ETH/wsuperOETHb
uint192 p = uoaPerTar.mul(targetPerTok);
uint192 err = p.mul(oracleError, CEIL);

high = p + err;
low = p - err;
// assert(low <= high); obviously true just by inspection

// {tar/ref} = {tar/tok} / {ref/tok} Get current market peg
// ETH/superOETHb = ETH/wsuperOETHb / superOETHb/wsuperOETHb
pegPrice = FIX_ONE;
julianmrodri marked this conversation as resolved.
Show resolved Hide resolved
}
}
27 changes: 27 additions & 0 deletions contracts/plugins/assets/origin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Origin Wrapped SuperOETH Collateral Plugin

## Summary

This plugin allows `wsuperOETH` holders on base to use their tokens as collateral in the Reserve Protocol.

`wsuperOETH` is an owned, upgradeable, ERC4626-wrapper around the `superOETH` token.

`wsuperOETH` collects the native `superOETH` yield.

`wsuperOETH` contract: <https://etherscan.io/token/https://etherscan.io/address/0x7fcd174e80f264448ebee8c88a7c4476aaf58ea6#code>

## Implementation

### Units

| tok | ref | target | UoA |
| ---------- | --------- | ------ | --- |
| wsuperOETH | superOETH | ETH | USD |

### refPerTok()

Since `wsuperOETH` is an ERC4626 wrapper, the `refPerTok()` is straightforward: `wsuperOETH.convertToAssets(10 ** wsuperOETH.decimals())`

### claimRewards()

There are no rewards to claim from `wsuperOETH`, all yield is already included in the ERC4626 asset appreciation.
8 changes: 5 additions & 3 deletions scripts/addresses/8453-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"wstETH": "0x8b4374005291B8FCD14C4E947604b2FB3C660A73",
"meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3",
"aeroUSDCeUSD": "0x9216CD5cA133aBBd23cc6F873bB4a95A78032db0",
"aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4"
"aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4",
"wsuperOETHb": "0x748Aba85bFe143078Faf7516e51D17e04f743162"
},
"erc20s": {
"COMP": "0x9e1028F5F1D5eDE59748FFceE5532509976840E0",
Expand All @@ -31,6 +32,7 @@
"meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313",
"aeroUSDCeUSD": "0xDB5b8cead52f77De0f6B5255f73F348AAf2CBb8D",
"AERO": "0x940181a94A35A4569E4529A3CDfB74e38FD98631",
"aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61"
"aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61",
"wsuperOETHb": "0x7FcD174E80f264448ebeE8c88a7C4476AAF58Ea6"
}
}
}
6 changes: 4 additions & 2 deletions scripts/addresses/base-4.0.0/8453-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
"collateral": {
"meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3",
"aeroUSDCeUSD": "0x9216CD5cA133aBBd23cc6F873bB4a95A78032db0",
"aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4"
"aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4",
"wsuperOETHb": "0x748Aba85bFe143078Faf7516e51D17e04f743162"
},
"erc20s": {
"meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313",
"aeroUSDCeUSD": "0xDB5b8cead52f77De0f6B5255f73F348AAf2CBb8D",
"AERO": "0x940181a94A35A4569E4529A3CDfB74e38FD98631",
"aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61"
"aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61",
"wsuperOETHb": "0x7FcD174E80f264448ebeE8c88a7C4476AAF58Ea6"
}
}
3 changes: 2 additions & 1 deletion scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ async function main() {
'phase2-assets/assets/deploy_stg.ts',
'phase2-assets/collaterals/deploy_morphoeUSD.ts',
'phase2-assets/collaterals/deploy_aerodrome_usdc_eusd.ts',
'phase2-assets/collaterals/deploy_aerodrome_usdz_usdc.ts'
'phase2-assets/collaterals/deploy_aerodrome_usdz_usdc.ts',
'phase2-assets/collaterals/deploy_origin_eth.ts'
)
} else if (chainId == '42161' || chainId == '421614') {
// Arbitrum One
Expand Down
98 changes: 98 additions & 0 deletions scripts/deployment/phase2-assets/collaterals/deploy_origin_eth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import fs from 'fs'
import hre from 'hardhat'
import { getChainId } from '../../../../common/blockchain-utils'
import { baseL2Chains, networkConfig } from '../../../../common/configuration'
import { bn, fp } from '../../../../common/numbers'
import { expect } from 'chai'
import { CollateralStatus } from '../../../../common/constants'
import {
BASE_PRICE_FEEDS,
BASE_ORACLE_ERROR,
BASE_FEEDS_TIMEOUT,
} from '../../../../test/plugins/individual-collateral/origin/constants'
import {
getDeploymentFile,
getAssetCollDeploymentFilename,
IAssetCollDeployments,
getDeploymentFilename,
fileExists,
} from '../../common'
import { priceTimeout } from '../../utils'
import { OETHCollateralL2Base } from '../../../../typechain'
import { ContractFactory } from 'ethers'

async function main() {
// ==== Read Configuration ====
const [deployer] = await hre.ethers.getSigners()

const chainId = await getChainId(hre)

console.log(`Deploying Origin ETH to network ${hre.network.name} (${chainId})
with burner account: ${deployer.address}`)

if (!networkConfig[chainId]) {
throw new Error(`Missing network configuration for ${hre.network.name}`)
}

// Get phase1 deployment
const phase1File = getDeploymentFilename(chainId)
if (!fileExists(phase1File)) {
throw new Error(`${phase1File} doesn't exist yet. Run phase 1`)
}
// Check previous step completed
const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId)
const assetCollDeployments = <IAssetCollDeployments>getDeploymentFile(assetCollDeploymentFilename)

const deployedCollateral: string[] = []

/******** Deploy Super Origin ETH Collateral - wsuperOETHb **************************/

// Only for Base
if (!baseL2Chains.includes(hre.network.name)) {
throw new Error(`Unsupported chainId: ${chainId}`)
}

const OETHCollateralL2BaseFactory: ContractFactory = await hre.ethers.getContractFactory(
'OETHCollateralL2Base'
)

const collateral = <OETHCollateralL2Base>await OETHCollateralL2BaseFactory.connect(
deployer
).deploy(
{
priceTimeout: priceTimeout.toString(),
chainlinkFeed: BASE_PRICE_FEEDS.wsuperOETHb_ETH, // ignored
oracleError: BASE_ORACLE_ERROR.toString(), // 0.5% + 0.5%
erc20: networkConfig[chainId].tokens.wsuperOETHb,
maxTradeVolume: fp('1e6').toString(), // $1m,
oracleTimeout: BASE_FEEDS_TIMEOUT.wsuperOETHb_ETH, // ignored
targetName: hre.ethers.utils.formatBytes32String('ETH'),
defaultThreshold: fp('0.02').add(BASE_ORACLE_ERROR).toString(),
delayUntilDefault: bn('86400').toString(), // 24h
},
fp('1e-4').toString(), // revenueHiding = 0.01%
BASE_PRICE_FEEDS.wsuperOETHb_ETH, // targetPerTokChainlinkFeed
BASE_PRICE_FEEDS.ETH_USD, // uoaPerTargetChainlinkFeed
BASE_FEEDS_TIMEOUT.ETH_USD // uoaPerTarget timeout
)
await collateral.deployed()
await (await collateral.refresh()).wait()
expect(await collateral.status()).to.equal(CollateralStatus.SOUND)

console.log(`Deployed Origin ETH to ${hre.network.name} (${chainId}): ${collateral.address}`)

assetCollDeployments.collateral.wsuperOETHb = collateral.address
assetCollDeployments.erc20s.wsuperOETHb = networkConfig[chainId].tokens.wsuperOETHb
deployedCollateral.push(collateral.address.toString())

fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2))

console.log(`Deployed collateral to ${hre.network.name} (${chainId})
New deployments: ${deployedCollateral}
Deployment file: ${assetCollDeploymentFilename}`)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
61 changes: 61 additions & 0 deletions scripts/verification/collateral-plugins/verify_origin_eth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import hre from 'hardhat'
import { getChainId } from '../../../common/blockchain-utils'
import { developmentChains, networkConfig } from '../../../common/configuration'
import { fp, bn } from '../../../common/numbers'
import {
getDeploymentFile,
getAssetCollDeploymentFilename,
IAssetCollDeployments,
} from '../../deployment/common'
import { priceTimeout, verifyContract } from '../../deployment/utils'
import {
BASE_PRICE_FEEDS,
BASE_ORACLE_ERROR,
BASE_FEEDS_TIMEOUT,
} from '../../../test/plugins/individual-collateral/origin/constants'

let deployments: IAssetCollDeployments

async function main() {
// ********** Read config **********
const chainId = await getChainId(hre)
if (!networkConfig[chainId]) {
throw new Error(`Missing network configuration for ${hre.network.name}`)
}

if (developmentChains.includes(hre.network.name)) {
throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`)
}

const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId)
deployments = <IAssetCollDeployments>getDeploymentFile(assetCollDeploymentFilename)

/******** Verify Origin ETH - wsuperOETHb **************************/
await verifyContract(
chainId,
deployments.collateral.wsuperOETHb,
[
{
priceTimeout: priceTimeout.toString(),
chainlinkFeed: BASE_PRICE_FEEDS.wsuperOETHb_ETH, // ignored
oracleError: BASE_ORACLE_ERROR.toString(), // 0.5% + 0.5%
erc20: networkConfig[chainId].tokens.wsuperOETHb,
maxTradeVolume: fp('1e6').toString(), // $1m,
oracleTimeout: BASE_FEEDS_TIMEOUT.wsuperOETHb_ETH, // ignored
targetName: hre.ethers.utils.formatBytes32String('ETH'),
defaultThreshold: fp('0.02').add(BASE_ORACLE_ERROR).toString(),
delayUntilDefault: bn('86400').toString(), // 24h
},
fp('1e-4').toString(), // revenueHiding = 0.01%
BASE_PRICE_FEEDS.wsuperOETHb_ETH, // targetPerTokChainlinkFeed
BASE_PRICE_FEEDS.ETH_USD, // uoaPerTargetChainlinkFeed
BASE_FEEDS_TIMEOUT.ETH_USD, // uoaPerTarget timeout
],
'contracts/plugins/assets/origin/OETHCollateralL2Base.sol:OETHCollateralL2Base'
)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
3 changes: 2 additions & 1 deletion scripts/verify_etherscan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ async function main() {
'assets/verify_stg.ts',
'collateral-plugins/verify_morphoeUSD.ts',
'collateral-plugins/verify_aerodrome_usdc_eusd.ts',
'collateral-plugins/verify_aerodrome_usdz_usdc.ts'
'collateral-plugins/verify_aerodrome_usdz_usdc.ts',
'collateral-plugins/verify_origin_eth.ts'
)
} else if (chainId == '42161' || chainId == '421614') {
// Arbitrum One
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ allStableTests.forEach((curr: AeroStablePoolEnumeration) => {
const coll = await deployCollateral({
pool: curr.pool,
gauge: curr.gauge,
chainlinkFeed: feed0.address,
feeds: [[feed0.address], [feed1.address]],
})

Expand Down Expand Up @@ -290,6 +291,7 @@ allStableTests.forEach((curr: AeroStablePoolEnumeration) => {
const coll = await deployCollateral({
pool: curr.pool,
gauge: curr.gauge,
chainlinkFeed: feed0.address,
feeds: [[feed0.address], [feed1.address]],
})

Expand Down Expand Up @@ -330,6 +332,7 @@ allStableTests.forEach((curr: AeroStablePoolEnumeration) => {
const invalidCollateral = await deployCollateral({
pool: curr.pool,
gauge: curr.gauge,
chainlinkFeed: invalidChainlinkFeed.address,
feeds: [[invalidChainlinkFeed.address], [invalidChainlinkFeed.address]],
})

Expand Down
Loading
Loading