Skip to content

Commit

Permalink
wstETH pricer (#440)
Browse files Browse the repository at this point in the history
* chore: packages

* add interfaces & mocks

* chore: update prettier package

* chore: linting test files

* fix: flashUnwrap unit test

* build: pin truffle to v5.1.32

* update interface

* add WSTETH price

* change mock from STETH to WSTETH

* update revert message

* unit-test: WstethPricer.sol

* fix typo

* refactor: shorten require error strings to error codes & document - WstethPricer

* feat: initial WstethPricer deployment script

* docs: update README w/ WstethPricer deployment steps

* docs: update docs & diagrams

* fix: lint issues

* refactor: rm redundant cast to address - WstethPricer

* docs: update code comments - WstethPricer

* docs: update

Co-authored-by: CruzMolina <[email protected]>
  • Loading branch information
haythem96 and CruzMolina authored Aug 20, 2021
1 parent 67a2bff commit 6f4f969
Show file tree
Hide file tree
Showing 85 changed files with 51,446 additions and 7,911 deletions.
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
* pricers
* [ChainlinkPricer](docs/contracts-documentation/pricers/ChainlinkPricer.md)
* [CompoundPricer](docs/contracts-documentation/pricers/CompoundPricer.md)
* [WstethPricer](docs/contracts-documentation/pricers/WstethPricer.md)
* [YearnPricer](docs/contracts-documentation/pricers/YearnPricer.md)
8 changes: 4 additions & 4 deletions contracts/core/Controller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ contract Controller is Initializable, OwnableUpgradeSafe, ReentrancyGuardUpgrade
/**
* @notice modifier to check if the system is not partially paused, where only redeem and settleVault is allowed
*/
modifier notPartiallyPaused {
modifier notPartiallyPaused() {
_isNotPartiallyPaused();

_;
Expand All @@ -224,7 +224,7 @@ contract Controller is Initializable, OwnableUpgradeSafe, ReentrancyGuardUpgrade
/**
* @notice modifier to check if the system is not fully paused, where no functionality is allowed
*/
modifier notFullyPaused {
modifier notFullyPaused() {
_isNotFullyPaused();

_;
Expand All @@ -233,7 +233,7 @@ contract Controller is Initializable, OwnableUpgradeSafe, ReentrancyGuardUpgrade
/**
* @notice modifier to check if sender is the fullPauser address
*/
modifier onlyFullPauser {
modifier onlyFullPauser() {
require(msg.sender == fullPauser, "C1");

_;
Expand All @@ -242,7 +242,7 @@ contract Controller is Initializable, OwnableUpgradeSafe, ReentrancyGuardUpgrade
/**
* @notice modifier to check if the sender is the partialPauser address
*/
modifier onlyPartialPauser {
modifier onlyPartialPauser() {
require(msg.sender == partialPauser, "C2");

_;
Expand Down
12 changes: 12 additions & 0 deletions contracts/interfaces/WSTETHInterface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.6.10;

interface WSTETHInterface {
function name() external view returns (string memory);

function symbol() external view returns (string memory);

function decimals() external view returns (uint8);

function stEthPerToken() external view returns (uint256);
}
2 changes: 1 addition & 1 deletion contracts/mocks/MockDumbERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract MockDumbERC20 {
return true;
}

function allowance(address owner, address spender) public virtual view returns (uint256) {
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}

Expand Down
21 changes: 21 additions & 0 deletions contracts/mocks/MockWSTETHToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.10;

import {ERC20Upgradeable} from "../packages/oz/upgradeability/ERC20Upgradeable.sol";

contract MockWSTETHToken is ERC20Upgradeable {
uint256 public stEthPerToken;

constructor(string memory _name, string memory _symbol) public {
__ERC20_init_unchained(_name, _symbol);
_setupDecimals(18);
}

function mint(address account, uint256 amount) public {
_mint(account, amount);
}

function setStEthPerToken(uint256 _stEthPerToken) external {
stEthPerToken = _stEthPerToken;
}
}
4 changes: 2 additions & 2 deletions contracts/packages/oz/Context.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ pragma solidity 0.6.10;
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal virtual view returns (address payable) {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}

function _msgData() internal virtual view returns (bytes memory) {
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
Expand Down
6 changes: 3 additions & 3 deletions contracts/packages/oz/upgradeability/ERC20Upgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeabl
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public override view returns (uint256) {
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}

/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public override view returns (uint256) {
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}

Expand All @@ -127,7 +127,7 @@ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeabl
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public virtual override view returns (uint256) {
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ abstract contract ContextUpgradeable is Initializable {

function __Context_init_unchained() internal initializer {}

function _msgSender() internal virtual view returns (address payable) {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}

function _msgData() internal virtual view returns (bytes memory) {
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
Expand Down
14 changes: 7 additions & 7 deletions contracts/packages/oz/upgradeability/Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ abstract contract Proxy {
* @dev Tells the address of the implementation where every call will be delegated.
* @return address of the implementation to which it will be delegated
*/
function implementation() public virtual view returns (address);
function implementation() public view virtual returns (address);

/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
Expand All @@ -30,12 +30,12 @@ abstract contract Proxy {
returndatacopy(ptr, 0, size)

switch result
case 0 {
revert(ptr, size)
}
default {
return(ptr, size)
}
case 0 {
revert(ptr, size)
}
default {
return(ptr, size)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract UpgradeabilityProxy is Proxy {
* @dev Tells the address of the current implementation
* @return impl address of the current implementation
*/
function implementation() public override view returns (address impl) {
function implementation() public view override returns (address impl) {
bytes32 position = implementationPosition;
assembly {
impl := sload(position)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ abstract contract EIP712Upgradeable is Initializable {
/* solhint-disable var-name-mixedcase */
bytes32 private _HASHED_NAME;
bytes32 private _HASHED_VERSION;
bytes32 private constant _TYPE_HASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

/* solhint-enable var-name-mixedcase */

Expand Down Expand Up @@ -102,7 +101,7 @@ abstract contract EIP712Upgradeable is Initializable {
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712NameHash() internal virtual view returns (bytes32) {
function _EIP712NameHash() internal view virtual returns (bytes32) {
return _HASHED_NAME;
}

Expand All @@ -112,7 +111,7 @@ abstract contract EIP712Upgradeable is Initializable {
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712VersionHash() internal virtual view returns (bytes32) {
function _EIP712VersionHash() internal view virtual returns (bytes32) {
return _HASHED_VERSION;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ abstract contract ERC20PermitUpgradeable is
/**
* @dev See {IERC20Permit-nonces}.
*/
function nonces(address owner) public override view returns (uint256) {
function nonces(address owner) public view override returns (uint256) {
return _nonces[owner].current();
}

/**
* @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external override view returns (bytes32) {
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/pricers/ChainlinkPricer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ contract ChainLinkPricer is OpynPricerInterface {
* @dev overides the getPrice function in OpynPricerInterface
* @return price of the asset in USD, scaled by 1e8
*/
function getPrice() external override view returns (uint256) {
function getPrice() external view override returns (uint256) {
(, int256 answer, , , ) = aggregator.latestRoundData();
require(answer > 0, "ChainLinkPricer: price is lower than 0");
// chainlink's answer is already 1e8
Expand All @@ -92,7 +92,7 @@ contract ChainLinkPricer is OpynPricerInterface {
* @param _roundId chainlink round id
* @return round price and timestamp
*/
function getHistoricalPrice(uint80 _roundId) external override view returns (uint256, uint256) {
function getHistoricalPrice(uint80 _roundId) external view override returns (uint256, uint256) {
(, int256 price, , uint256 roundTimestamp, ) = aggregator.getRoundData(_roundId);
return (_scaleToBase(uint256(price)), roundTimestamp);
}
Expand Down
92 changes: 92 additions & 0 deletions contracts/pricers/WstethPricer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.6.10;

import {OracleInterface} from "../interfaces/OracleInterface.sol";
import {OpynPricerInterface} from "../interfaces/OpynPricerInterface.sol";
import {WSTETHInterface} from "../interfaces/WSTETHInterface.sol";
import {SafeMath} from "../packages/oz/SafeMath.sol";

/**
* Error Codes
* W1: cannot deploy pricer, wstETH address cannot be 0
* W2: cannot deploy pricer, underlying address cannot be 0
* W3: cannot deploy pricer, oracle address cannot be 0
* W4: cannot retrieve price, underlying price is 0
* W5: cannot set expiry price in oracle, underlying price is 0 and has not been set
* W6: cannot retrieve historical prices, getHistoricalPrice has been deprecated
*/

/**
* @title WstethPricer
* @author Opyn Team
* @notice A Pricer contract for a wstETH token
*/
contract WstethPricer is OpynPricerInterface {
using SafeMath for uint256;

/// @notice opyn oracle address
OracleInterface public oracle;

/// @notice wstETH token
WSTETHInterface public wstETH;

/// @notice underlying asset (WETH)
address public underlying;

/**
* @param _wstETH wstETH
* @param _underlying underlying asset for wstETH
* @param _oracle Opyn Oracle contract address
*/
constructor(
address _wstETH,
address _underlying,
address _oracle
) public {
require(_wstETH != address(0), "W1");
require(_underlying != address(0), "W2");
require(_oracle != address(0), "W3");

wstETH = WSTETHInterface(_wstETH);
oracle = OracleInterface(_oracle);
underlying = _underlying;
}

/**
* @notice get the live price for the asset
* @dev overrides the getPrice function in OpynPricerInterface
* @return price of 1 wstETH in USD, scaled by 1e8
*/
function getPrice() external view override returns (uint256) {
uint256 underlyingPrice = oracle.getPrice(underlying);
require(underlyingPrice > 0, "W4");
return _underlyingPriceToWstethPrice(underlyingPrice);
}

/**
* @notice set the expiry price in the oracle
* @dev requires that the underlying price has been set before setting a wstETH price
* @param _expiryTimestamp expiry to set a price for
*/
function setExpiryPriceInOracle(uint256 _expiryTimestamp) external {
(uint256 underlyingPriceExpiry, ) = oracle.getExpiryPrice(underlying, _expiryTimestamp);
require(underlyingPriceExpiry > 0, "W5");
uint256 wstEthPrice = _underlyingPriceToWstethPrice(underlyingPriceExpiry);
oracle.setExpiryPrice(address(wstETH), _expiryTimestamp, wstEthPrice);
}

/**
* @dev convert underlying price to wstETH price with the wstETH to stETH exchange rate (1 stETH ≈ 1 ETH)
* @param _underlyingPrice price of 1 underlying token (ie 1e18 WETH) in USD, scaled by 1e8
* @return price of 1 wstETH in USD, scaled by 1e8
*/
function _underlyingPriceToWstethPrice(uint256 _underlyingPrice) private view returns (uint256) {
uint256 stEthPerWsteth = wstETH.stEthPerToken();

return stEthPerWsteth.mul(_underlyingPrice).div(1e18);
}

function getHistoricalPrice(uint80) external view override returns (uint256, uint256) {
revert("W6");
}
}
4 changes: 2 additions & 2 deletions contracts/pricers/YearnPricer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ contract YearnPricer is OpynPricerInterface {
* @dev overrides the getPrice function in OpynPricerInterface
* @return price of 1e8 yToken in USD, scaled by 1e8
*/
function getPrice() external override view returns (uint256) {
function getPrice() external view override returns (uint256) {
uint256 underlyingPrice = oracle.getPrice(address(underlying));
require(underlyingPrice > 0, "YearnPricer: underlying price is 0");
return _underlyingPriceToYtokenPrice(underlyingPrice);
Expand Down Expand Up @@ -76,7 +76,7 @@ contract YearnPricer is OpynPricerInterface {
return pricePerShare.mul(_underlyingPrice).div(10**uint256(underlyingDecimals));
}

function getHistoricalPrice(uint80 _roundId) external override view returns (uint256, uint256) {
function getHistoricalPrice(uint80 _roundId) external view override returns (uint256, uint256) {
revert("YearnPricer: Deprecated");
}
}
4 changes: 2 additions & 2 deletions docs/contracts-documentation/core/Controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ use donate() instead of direct transfer() to store the balance in assetBalance

allows the partialPauser to toggle the systemPartiallyPaused variable and partially pause or partially unpause the system

can only be called by the pauser
can only be called by the partialPauser

#### Parameters:

Expand All @@ -242,7 +242,7 @@ can only be called by the pauser

allows the fullPauser to toggle the systemFullyPaused variable and fully pause or fully unpause the system

can only be called by the pauser
can only be called by the fullyPauser

#### Parameters:

Expand Down
19 changes: 19 additions & 0 deletions docs/contracts-documentation/interfaces/WSTETHInterface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `WSTETHInterface`

## Functions:

- `name() (external)`

- `symbol() (external)`

- `decimals() (external)`

- `stEthPerToken() (external)`

### Function `name() → string external`

### Function `symbol() → string external`

### Function `decimals() → uint8 external`

### Function `stEthPerToken() → uint256 external`
Loading

0 comments on commit 6f4f969

Please sign in to comment.