Skip to content

Latest commit

 

History

History
138 lines (96 loc) · 7.33 KB

README.md

File metadata and controls

138 lines (96 loc) · 7.33 KB

v1-core

Lint Python Lint Solidity Tests

V1 core smart contracts

Requirements

To run the project you need:

  • Python >= 3.9.2
  • Brownie >= 1.17.2
  • Local Ganache environment installed
  • .env file in project root with format
# required environment variables
export WEB3_INFURA_PROJECT_ID=<INFURA_TOKEN>
export ARBISCAN_TOKEN=<ETHERSCAN_TOKEN>
# add Arbitrum Fork
brownie networks add Development arbitrum-main-fork name="Ganache-CLI (Aribtrum-Mainnet Fork)" host=http://127.0.0.1 cmd=ganache-cli accounts=10 evm_version=istanbul fork=arbitrum-main mnemonic=brownie port=8545
# modify network configuration to use API key
brownie networks modify arbitrum-main host="https://arbitrum-mainnet.infura.io/v3/\$WEB3_INFURA_PROJECT_ID" provider=infura

To generate the required tokens, see

Diagram

diagram

Modules

V1 core relies on three modules:

Markets Module

Traders interact directly with the market contract to take positions on a data stream. Core functions are:

  • build()
  • unwind()
  • liquidate()
  • update()

Traders transfer OV collateral to the market contract to back a position. This collateral is held in the market contract until the trader unwinds their position when exiting the trade. OV is the only collateral supported for V1.

The market contract tracks the current open interest for all outstanding positions on a market as well as information about each position, that is needed in order to calculate the current value of the position in OV terms:

library Position {
    /// @dev immutables: notionalInitial, debtInitial, midTick, entryTick, isLong
    /// @dev mutables: liquidated, oiShares, fractionRemaining
    struct Info {
        uint96 notionalInitial; // initial notional = collateral * leverage
        uint96 debtInitial; // initial debt = notional - collateral
        int24 midTick; // midPrice = 1.0001 ** midTick at build
        int24 entryTick; // entryPrice = 1.0001 ** entryTick at build
        bool isLong; // whether long or short
        bool liquidated; // whether has been liquidated (mutable)
        uint240 oiShares; // current shares of aggregate open interest on side (mutable)
        uint16 fractionRemaining; // fraction of initial position remaining (mutable)
    }
}

For each market contract, there is an associated feed contract that delivers the data from the data stream. The market contract stores a pointer to the feed contract that it retrieves new data from, and the market uses the feed's update() function to retrieve the most recent price and liquidity data from the feed through a call to IOverlayV1Feed(feed).latest(). This call occurs every time a user interacts with the market.

All markets are implemented by the contract OverlayV1Market.sol, regardless of the underlying feed type.

Feeds Module

The feed contract ingests the data stream directly from the oracle provider and formats the data in a format consumable by any market contract. The feed contract is limited to a single core external view function

  • latest()

and an internal view function

  • _fetch()

which is implemented differently for each specific oracle type. When adding support for a new type of oracle, developers must create a new feed contract that inherits from OverlayV1Feed.sol and implement the internal function _fetch() to properly integrate with the oracle provider (e.g. Uniswap V3, Chainlink, Balancer V2).

View data returned by latest() is formatted as specified by Oracle.Data:

library Oracle {
  struct Data {
      uint256 timestamp;
      uint256 microWindow;
      uint256 macroWindow;
      uint256 priceOverMicroWindow; // p(now) averaged over micro
      uint256 priceOverMacroWindow; // p(now) averaged over macro
      uint256 priceOneMacroWindowAgo; // p(now - macro) avg over macro
      uint256 reserveOverMicroWindow; // r(now) in ov averaged over micro
      bool hasReserve; // whether oracle has manipulable reserve pool
  }
}

from the Oracle.sol library. Oracle.Data is consumed by each deployment of OverlayV1Market.sol for traders to take positions on the market of interest.

For each oracle provider supported, there should be a specific implementation of a feed contract that inherits from OverlayV1Feed.sol (e.g. OverlayV1UniswapV3Feed.sol for Uniswap V3 pools).

OV Module

The OV module consists of an ERC20 token with permissioned mint and burn functions. Upon initialization, markets must be given permission to mint and burn OV to compensate traders for their PnL on positions.

OverlayV1Factory.sol grants these mint and burn permissions on a call to deployMarket(). Because of this, the factory contract must have admin privileges on the OV token prior to deploying markets.

Deployment Process

The process to add a new market is as follows:

  1. Deploy a feed contract for the data stream, if not already deployed. Developers inherit from OverlayV1Feed.sol to implement a feed contract for the specific type of oracle provider they are looking to support if it hasn't already been implemented (e.g. OverlayV1UniswapV3Feed.sol for Uniswap V3 pools). The feed contract ingests the data stream directly from the oracle provider and formats the data in a form consumable by the market.

  2. Deploy an OverlayV1Market.sol contract referencing the previously deployed feed from 1 as the feed constructor parameter. This is accomplished by governance calling deployMarket() on the market factory contract OverlayV1Factory.sol. Traders interact directly with the newly deployed market contract to take positions out. The market contract stores the active positions and open interest for all outstanding trades on the data stream.

  3. The market factory contract grants the newly deployed market contract mint and burn privileges on the sole instance of the OverlayV1Token.sol token. Governance should grant the market factory contract admin privileges on the OV token prior to any markets being deployed, otherwise deployMarket() will revert.