Skip to content

Commit

Permalink
Add estimates for position attributes on build (#22)
Browse files Browse the repository at this point in the history
* Rename fee recipient to fee disperser

* Update package-lock.json

* Add estimate state views

* Fix estimatePosition, add test for position estimate

* Fix: solidity lint

* Add tests for estimate sans liq price

* Add test for liq price estimate

* Rm todo comment
  • Loading branch information
mikeyrf authored May 6, 2022
1 parent 1b429c5 commit f1ee10e
Show file tree
Hide file tree
Showing 16 changed files with 591 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "./interfaces/uniswap/v3-staker/IUniswapV3Staker.sol";
import "./libraries/uniswap/v3-staker/IncentiveId.sol";

contract OverlayV1FeeRecipient {
contract OverlayV1FeeDisperser {
using FixedPoint for uint256;

IOverlayV1Token public immutable ovl; // overlay token
Expand Down
2 changes: 2 additions & 0 deletions contracts/OverlayV1State.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.10;
import "./interfaces/IOverlayV1State.sol";

import "./state/OverlayV1BaseState.sol";
import "./state/OverlayV1EstimateState.sol";
import "./state/OverlayV1OIState.sol";
import "./state/OverlayV1PositionState.sol";
import "./state/OverlayV1PriceState.sol";
Expand All @@ -15,6 +16,7 @@ contract OverlayV1State is
OverlayV1BaseState,
OverlayV1PriceState,
OverlayV1OIState,
OverlayV1EstimateState,
OverlayV1PositionState
{
constructor(IOverlayV1Factory _factory) OverlayV1BaseState(_factory) {}
Expand Down
59 changes: 59 additions & 0 deletions contracts/interfaces/state/IOverlayV1EstimateState.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "@overlay/v1-core/contracts/interfaces/IOverlayV1Market.sol";
import "@overlay/v1-core/contracts/libraries/Position.sol";

import "./IOverlayV1BaseState.sol";
import "./IOverlayV1OIState.sol";
import "./IOverlayV1PriceState.sol";

interface IOverlayV1EstimateState is IOverlayV1BaseState, IOverlayV1PriceState, IOverlayV1OIState {
// estimated position to be built on the market
function positionEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (Position.Info memory position_);

// estimated debt of position on the market
function debtEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 debt_);

// estimated cost basis of position on the market
function costEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 cost_);

// estimated open interest of position on the market
function oiEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 oi_);

// estimated maintenance margin requirement for position on market
function maintenanceMarginEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 maintenanceMargin_);

// estimated liquidation price for position on market
function liquidationPriceEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 liquidationPrice_);
}
231 changes: 231 additions & 0 deletions contracts/state/OverlayV1EstimateState.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "@overlay/v1-core/contracts/interfaces/IOverlayV1Market.sol";
import "@overlay/v1-core/contracts/libraries/FixedPoint.sol";
import "@overlay/v1-core/contracts/libraries/Oracle.sol";
import "@overlay/v1-core/contracts/libraries/Position.sol";
import "@overlay/v1-core/contracts/libraries/Risk.sol";

import "../interfaces/state/IOverlayV1EstimateState.sol";

import "./OverlayV1BaseState.sol";
import "./OverlayV1OIState.sol";
import "./OverlayV1PriceState.sol";

abstract contract OverlayV1EstimateState is
IOverlayV1EstimateState,
OverlayV1BaseState,
OverlayV1PriceState,
OverlayV1OIState
{
using FixedPoint for uint256;
using Position for Position.Info;

/// @notice Gets the position that would be built on the given market
/// @notice for the given (collateral, leverage, isLong) attributes
function _estimatePosition(
IOverlayV1Market market,
Oracle.Data memory data,
uint256 collateral,
uint256 leverage,
bool isLong
) internal view returns (Position.Info memory position_) {
// notional, debt, oi
uint256 notional = collateral.mulUp(leverage);
uint256 debt = notional - collateral;
uint256 oi = _oiFromNotional(data, notional);
uint256 fractionOfCapOi = _fractionOfCapOi(market, data, oi);

// prices
uint256 midPrice = _mid(data);
uint256 price = isLong
? _ask(market, data, fractionOfCapOi)
: _bid(market, data, fractionOfCapOi);
position_ = Position.Info({
notional: uint96(notional),
debt: uint96(debt),
entryToMidRatio: Position.calcEntryToMidRatio(price, midPrice),
isLong: isLong,
liquidated: false,
oiShares: oi
});
}

function _debtEstimate(Position.Info memory position) internal view returns (uint256 debt_) {
// assume entire position value such that fraction = ONE
uint256 fraction = FixedPoint.ONE;

// debt estimate is simply initial debt of position
debt_ = position.debtCurrent(fraction);
}

function _costEstimate(Position.Info memory position) internal view returns (uint256 cost_) {
// assume entire position value such that fraction = ONE
uint256 fraction = FixedPoint.ONE;

// cost estimate is simply initial cost of position
cost_ = position.cost(fraction);
}

function _oiEstimate(Position.Info memory position) internal view returns (uint256 oi_) {
// assume entire position value such that fraction = ONE
uint256 fraction = FixedPoint.ONE;

// oi estimate is simply initial oi of position
oi_ = position.oiInitial(fraction);
}

function _maintenanceMarginEstimate(IOverlayV1Market market, Position.Info memory position)
internal
view
returns (uint256 maintenanceMargin_)
{
uint256 maintenanceMarginFraction = market.params(
uint256(Risk.Parameters.MaintenanceMarginFraction)
);
uint256 q = position.notionalInitial(FixedPoint.ONE);
maintenanceMargin_ = q.mulUp(maintenanceMarginFraction);
}

function _liquidationPriceEstimate(IOverlayV1Market market, Position.Info memory position)
internal
view
returns (uint256 liquidationPrice_)
{
// get position attributes independent of funding
uint256 entryPrice = position.entryPrice();
uint256 liquidationFeeRate = market.params(uint256(Risk.Parameters.LiquidationFeeRate));
uint256 maintenanceMargin = _maintenanceMarginEstimate(market, position);

// get position attributes
// NOTE: cost is same as initial collateral
uint256 oi = _oiEstimate(position);
uint256 collateral = _costEstimate(position);
require(oi > 0, "OVLV1: oi == 0");

// get price delta from entry price: dp = | liqPrice - entryPrice |
uint256 dp = collateral
.subFloor(maintenanceMargin.divUp(FixedPoint.ONE - liquidationFeeRate))
.divUp(oi);
liquidationPrice_ = position.isLong ? entryPrice.subFloor(dp) : entryPrice + dp;
}

/// @notice Gets the estimated position to be built on the Overlay market
/// @notice for the given (collateral, leverage, isLong) attributes
function positionEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (Position.Info memory position_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
position_ = _estimatePosition(market, data, collateral, leverage, isLong);
}

/// @notice Gets the estimated debt of the position to be built
/// @notice on the Overlay market for the given (collateral, leverage,
/// @notice isLong) attributes
function debtEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 debt_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
Position.Info memory position = _estimatePosition(
market,
data,
collateral,
leverage,
isLong
);
debt_ = _debtEstimate(position);
}

/// @notice Gets the estimated cost of the position to be built
/// @notice on the Overlay market for the given (collateral, leverage,
/// @notice isLong) attributes
function costEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 cost_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
Position.Info memory position = _estimatePosition(
market,
data,
collateral,
leverage,
isLong
);
cost_ = _costEstimate(position);
}

/// @notice Gets the estimated open interest of the position to be built
/// @notice on the Overlay market for the given (collateral, leverage,
/// @notice isLong) attributes
function oiEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 oi_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
Position.Info memory position = _estimatePosition(
market,
data,
collateral,
leverage,
isLong
);
oi_ = _oiEstimate(position);
}

/// @notice Gets the estimated maintenance margin of the position to be built
/// @notice on the Overlay market for the given (collateral, leverage, isLong)
/// @notice attributes
function maintenanceMarginEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 maintenanceMargin_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
Position.Info memory position = _estimatePosition(
market,
data,
collateral,
leverage,
isLong
);
maintenanceMargin_ = _maintenanceMarginEstimate(market, position);
}

/// @notice Gets the estimated liquidation price of the position to be built
/// @notice on the Overlay market for the given (collateral, leverage, isLong)
/// @notice attributes
function liquidationPriceEstimate(
IOverlayV1Market market,
uint256 collateral,
uint256 leverage,
bool isLong
) external view returns (uint256 liquidationPrice_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
Position.Info memory position = _estimatePosition(
market,
data,
collateral,
leverage,
isLong
);
liquidationPrice_ = _liquidationPriceEstimate(market, position);
}
}
1 change: 1 addition & 0 deletions contracts/state/OverlayV1OIState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.10;
import "@overlay/v1-core/contracts/interfaces/IOverlayV1Market.sol";
import "@overlay/v1-core/contracts/libraries/FixedPoint.sol";
import "@overlay/v1-core/contracts/libraries/Oracle.sol";
import "@overlay/v1-core/contracts/libraries/Risk.sol";
import "@overlay/v1-core/contracts/libraries/Roller.sol";

import "../interfaces/state/IOverlayV1OIState.sol";
Expand Down
2 changes: 1 addition & 1 deletion contracts/state/OverlayV1PositionState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "@overlay/v1-core/contracts/interfaces/IOverlayV1Market.sol";
import "@overlay/v1-core/contracts/libraries/FixedPoint.sol";
import "@overlay/v1-core/contracts/libraries/Oracle.sol";
import "@overlay/v1-core/contracts/libraries/Position.sol";
import "@overlay/v1-core/contracts/libraries/Risk.sol";

import "../interfaces/state/IOverlayV1PositionState.sol";

Expand Down Expand Up @@ -491,7 +492,6 @@ abstract contract OverlayV1PositionState is
uint256 id
) external view returns (uint256 liquidationPrice_) {
address feed = market.feed();
Oracle.Data memory data = _getOracleData(feed);
Position.Info memory position = _getPosition(market, owner, id);

// get position attributes independent of funding
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
Loading

0 comments on commit f1ee10e

Please sign in to comment.