-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
baad94d
commit a3c7b0f
Showing
17 changed files
with
749 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
9 changes: 9 additions & 0 deletions
9
contracts/plugins/assets/ethx/vendor/IStaderStakePoolManager.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"assets": {}, | ||
"collateral": { | ||
"ETHx": "0x73a36258E6A48D0095D1997Fec7F51e191B4Ec81" | ||
}, | ||
"erc20s": { | ||
"ETHx": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.