Skip to content

Commit

Permalink
Ethx plugin (#1183)
Browse files Browse the repository at this point in the history
Co-authored-by: Akshat Mittal <itsreallyakshat@gmail.com>
Co-authored-by: Dulguun <99845398+dulguun-staderlabs@users.noreply.github.com>
Co-authored-by: Taylor Brent <taylor.w.brent@gmail.com>
  • Loading branch information
4 people authored Aug 20, 2024
1 parent baad94d commit a3c7b0f
Show file tree
Hide file tree
Showing 17 changed files with 749 additions and 3 deletions.
3 changes: 3 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface ITokens {
stETH?: string
wstETH?: string
rETH?: string
ETHx?: string
cUSDCv3?: string
wcUSDCv3?: string
cUSDbCv3?: string
Expand Down Expand Up @@ -221,6 +222,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
stETH: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84',
wstETH: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0',
rETH: '0xae78736Cd615f374D3085123A210448E74Fc6393',
ETHx: '0xA35b1B31Ce002FBF2058D22F30f95D405200A15b',
cUSDCv3: '0xc3d688B66703497DAA19211EEdff47f25384cdc3',
wcUSDCv3: '0x27F2f159Fe990Ba83D57f39Fd69661764BEbf37a',
ONDO: '0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3',
Expand Down Expand Up @@ -268,6 +270,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
stETHETH: '0x86392dc19c0b719886221c78ab11eb8cf5c52812', // stETH/ETH
stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD
rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH
ETHx: '0xC5f8c4aB091Be1A899214c0C3636ca33DcA0C547', // ETHx/ETH
cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH
frxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df', // frxETH/ETH
pyUSD: '0x8f1dF6D7F2db73eECE86a18b4381F4707b918FB1',
Expand Down
78 changes: 78 additions & 0 deletions contracts/plugins/assets/ethx/ETHxCollateral.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { CEIL, FixLib, _safeWrap } from "../../../libraries/Fixed.sol";
import { AggregatorV3Interface, OracleLib } from "../OracleLib.sol";
import { CollateralConfig, AppreciatingFiatCollateral } from "../AppreciatingFiatCollateral.sol";
import { IStaderStakePoolManager } from "./vendor/IStaderStakePoolManager.sol";
import { IETHx } from "./vendor/IETHx.sol";

/**
* @title Stader ETHx collateral
* @notice Collateral plugin for Stader ETHx
* tok = ETHx
* ref = ETH2
* tar = ETH
* UoA = USD
*/
contract ETHxCollateral is AppreciatingFiatCollateral {
using OracleLib for AggregatorV3Interface;
using FixLib for uint192;

AggregatorV3Interface public immutable targetPerTokChainlinkFeed;
uint48 public immutable targetPerTokChainlinkTimeout;

/// @param config.chainlinkFeed {UoA/target} price of ETH in USD terms
/// @param _targetPerTokChainlinkFeed {target/tok} price of ETHx in ETH terms
constructor(
CollateralConfig memory config,
uint192 revenueHiding,
AggregatorV3Interface _targetPerTokChainlinkFeed,
uint48 _targetPerTokChainlinkTimeout
) AppreciatingFiatCollateral(config, revenueHiding) {
require(address(_targetPerTokChainlinkFeed) != address(0), "missing targetPerTok feed");
require(_targetPerTokChainlinkTimeout != 0, "targetPerTokChainlinkTimeout zero");
require(config.defaultThreshold != 0, "defaultThreshold zero");

targetPerTokChainlinkFeed = _targetPerTokChainlinkFeed;
targetPerTokChainlinkTimeout = _targetPerTokChainlinkTimeout;
maxOracleTimeout = uint48(Math.max(maxOracleTimeout, _targetPerTokChainlinkTimeout));
}

/// 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
)
{
uint192 targetPerTok = targetPerTokChainlinkFeed.price(targetPerTokChainlinkTimeout);

// {UoA/tok} = {UoA/target} * {target/tok}
uint192 p = chainlinkFeed.price(oracleTimeout).mul(targetPerTok);
uint192 err = p.mul(oracleError, CEIL);

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

// {target/ref} = {target/tok} / {ref/tok}
pegPrice = targetPerTok.div(underlyingRefPerTok());
}

/// @return {ref/tok} Quantity of whole reference units per whole collateral tokens
function underlyingRefPerTok() public view override returns (uint192) {
IStaderStakePoolManager staderStakePoolManager = IStaderStakePoolManager(
IETHx(address(erc20)).staderConfig().getStakePoolManager()
);
return _safeWrap(staderStakePoolManager.getExchangeRate());
}
}
26 changes: 26 additions & 0 deletions contracts/plugins/assets/ethx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Collateral Plugin - Stader - ETHx

## Summary

This plugin allows `ETHx` holders use their tokens as collateral in the Reserve Protocol.

## Implementation

### Units

| tok | ref | target | UoA |
| ---- | ---- | ------ | --- |
| ETHx | ETH2 | ETH | USD |

### refPerTok()

Gets the exchange rate for `ETHx` to `ETH2` from the ETHx token contract using the [getExchangeRate()]()
function. This is the rate used by stader labs when converting between ethx and eth2 and is closely followed by secondary markets.
While the value of ETH2/ETHx **should** be only-increasing, it is possible that slashing or inactivity events could occur for the ETHx
validators. As such, `ETHx` inherits `AppreciatingFiatCollateral` to allow for some amount of revenue-hiding. The amount of
revenue-hiding should be determined by the deployer, but can likely be quite high, as it is more likely that any dips, however large,
would be temporary, and, in particularly bad instances, be covered by the Stader protocol.

### claimRewards()

There are no rewards to claim from ETHx.
9 changes: 9 additions & 0 deletions contracts/plugins/assets/ethx/vendor/IETHx.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IStaderConfig } from "./IStaderConfig.sol";

interface IETHx is IERC20Metadata {
function staderConfig() external view returns (IStaderConfig);
}
6 changes: 6 additions & 0 deletions contracts/plugins/assets/ethx/vendor/IStaderConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

interface IStaderConfig {
function getStakePoolManager() external view returns (address);
}
41 changes: 41 additions & 0 deletions contracts/plugins/assets/ethx/vendor/IStaderOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

/// @title ExchangeRate
/// @notice This struct holds data related to the exchange rate between ETH and ETHX.
struct ExchangeRate {
/// @notice The block number when the exchange rate was last updated.
uint256 reportingBlockNumber;
/// @notice The total balance of Ether (ETH) in the system.
uint256 totalETHBalance;
/// @notice The total supply of the liquid staking token (ETHX) in the system.
uint256 totalETHXSupply;
}

struct SDPriceData {
uint256 reportingBlockNumber;
uint256 sdPriceInETH;
}

/// @title ValidatorStats
/// @notice This struct holds statistics related to validators in the beaconchain.
struct ValidatorStats {
/// @notice The block number when the validator stats was last updated.
uint256 reportingBlockNumber;
/// @notice The total balance of all exiting validators.
uint128 exitingValidatorsBalance;
/// @notice The total balance of all exited validators.
uint128 exitedValidatorsBalance;
/// @notice The total balance of all slashed validators.
uint128 slashedValidatorsBalance;
/// @notice The number of currently exiting validators.
uint32 exitingValidatorsCount;
/// @notice The number of validators that have exited.
uint32 exitedValidatorsCount;
/// @notice The number of validators that have been slashed.
uint32 slashedValidatorsCount;
}

interface IStaderOracle {
function getExchangeRate() external view returns (ExchangeRate memory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

interface IStaderStakePoolManager {
/**
* @notice returns the amount of ETH equivalent 1 ETHX (with 18 decimals)
*/
function getExchangeRate() external view returns (uint256);
}
70 changes: 70 additions & 0 deletions contracts/plugins/assets/ethx/vendor/StaderOracleMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "./IStaderConfig.sol";
import "./IStaderOracle.sol";
import "./IStaderStakePoolManager.sol";

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

contract StaderOracleMock is
IStaderOracle,
AccessControlUpgradeable,
PausableUpgradeable,
ReentrancyGuardUpgradeable
{
bool public erInspectionMode;
bool public isPORFeedBasedERData;
SDPriceData public lastReportedSDPriceData;
IStaderConfig public staderConfig;
ExchangeRate public inspectionModeExchangeRate;
ExchangeRate public exchangeRate;
ValidatorStats public validatorStats;

uint256 public constant MAX_ER_UPDATE_FREQUENCY = 7200 * 7; // 7 days
uint256 public constant ER_CHANGE_MAX_BPS = 10000;
uint256 public erChangeLimit;
uint256 public constant MIN_TRUSTED_NODES = 5;
uint256 public trustedNodeChangeCoolingPeriod;

uint256 public trustedNodesCount;
uint256 public lastReportedMAPDIndex;
uint256 public erInspectionModeStartBlock;
uint256 public lastTrustedNodeCountChangeBlock;

// indicate the health of protocol on beacon chain
// enabled by `MANAGER` if heavy slashing on protocol on beacon chain
bool public safeMode;

mapping(address => bool) public isTrustedNode;
mapping(bytes32 => bool) private nodeSubmissionKeys;
mapping(bytes32 => uint8) private submissionCountKeys;
mapping(bytes32 => uint16) public missedAttestationPenalty;
mapping(uint8 => uint256) public lastReportingBlockNumberForWithdrawnValidatorsByPoolId;
mapping(uint8 => uint256) public lastReportingBlockNumberForValidatorVerificationDetailByPoolId;

uint256[] private sdPrices;

bytes32 public constant ETHX_ER_UF = keccak256("ETHX_ER_UF");
bytes32 public constant SD_PRICE_UF = keccak256("SD_PRICE_UF");
bytes32 public constant VALIDATOR_STATS_UF = keccak256("VALIDATOR_STATS_UF");
bytes32 public constant WITHDRAWN_VALIDATORS_UF = keccak256("WITHDRAWN_VALIDATORS_UF");
bytes32 public constant MISSED_ATTESTATION_PENALTY_UF =
keccak256("MISSED_ATTESTATION_PENALTY_UF");
// Ready to Deposit Validators Update Frequency Key
bytes32 public constant VALIDATOR_VERIFICATION_DETAIL_UF =
keccak256("VALIDATOR_VERIFICATION_DETAIL_UF");
mapping(bytes32 => uint256) public updateFrequencyMap;

function getExchangeRate() external view override returns (ExchangeRate memory) {
return (exchangeRate);
}

// Mock function to be able to override rate in tests
function setExchangeRate(ExchangeRate memory newExchangeRate) external {
exchangeRate = newExchangeRate;
}
}
6 changes: 4 additions & 2 deletions scripts/addresses/1-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"Re7WETH": "0xa0a6C06e45437d4Ae1D778AaeB4605AC2B62A870",
"cvxCrvUSDUSDC": "0x9Fc0F31e2D26C437461a9eEBfe858d17e2611Ea5",
"cvxCrvUSDUSDT": "0x69c6597690B8Df61D15F201519C03725bdec40c1",
"sfrxETH": "0x4c891fCa6319d492866672E3D2AfdAAA5bDcfF67"
"sfrxETH": "0x4c891fCa6319d492866672E3D2AfdAAA5bDcfF67",
"ETHx": "0x73a36258E6A48D0095D1997Fec7F51e191B4Ec81"
},
"erc20s": {
"stkAAVE": "0x4da27a545c0c5B758a6BA100e3a049001de870f5",
Expand Down Expand Up @@ -117,6 +118,7 @@
"cvxCrvUSDUSDT": "0x5d1B749bA7f689ef9f260EDC54326C48919cA88b",
"sfrxETH": "0xac3E018457B222d93114458476f3E3416Abbe38F",
"CRV": "0xD533a949740bb3306d119CC777fa900bA034cd52",
"CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B"
"CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B",
"ETHx": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b"
}
}
9 changes: 9 additions & 0 deletions scripts/addresses/mainnet-4.0.0/1-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"assets": {},
"collateral": {
"ETHx": "0x73a36258E6A48D0095D1997Fec7F51e191B4Ec81"
},
"erc20s": {
"ETHx": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b"
}
}
1 change: 1 addition & 0 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ async function main() {
'phase2-assets/collaterals/deploy_steakpyusd.ts',
'phase2-assets/collaterals/deploy_bbusdt.ts',
'phase2-assets/collaterals/deploy_re7weth.ts',
'phase2-assets/collaterals/deploy_ethx_collateral.ts',
'phase2-assets/assets/deploy_crv.ts',
'phase2-assets/assets/deploy_cvx.ts'
)
Expand Down
Loading

0 comments on commit a3c7b0f

Please sign in to comment.