# Oracle Maker

{% hint style="danger" %}
Liquidity provision is whitelisted until initial beta testing has completed.
{% endhint %}

## Overview

Oracle maker uses price feeds from Pyth and may employ feeds from Chainlink and other providers as they become available/needed.&#x20;

* Each market backed by an oracle maker has a discrete liquidity pool.&#x20;
* Market orders with oracle pools are fill-or-kill, ie. no partial fills unless this logic is handled at a higher layer in the stack (e.g. by the [order-gateway](https://docs.nekodex.org/nekodex-playground/docs-for-devs/contracts/order-gateway "mention") for limit orders filled by oracle maker pools).
* All orders sent to an oracle maker pool must include a delay for front running protection - this can be handled by the order gateway.&#x20;

### Filling taker orders

<figure><img src="https://3534130831-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ft9pvATmvHXBuirxBUKA6%2Fuploads%2F6sYMIXLrgSS1VCHnJ8up%2Fimage.png?alt=media&#x26;token=39a0c63d-6fcb-4a7c-8358-75dd40ebfff1" alt=""><figcaption></figcaption></figure>

#### Order filled event

{% code overflow="wrap" %}

```solidity
/// @notice Emitted when an order is filled by a Pyth Oracle Maker. 
///         It reveals all information associated with the trade price.
event PythOracleOrderFilled(
    uint256 orcalePrice,    // In quote asset as wei
                            // Assumes price >= 0
    uint256 tradePrice,     // In quote asset as wei
                            // Assumes price >= 0
    uint256 size,           // In base asset as wei
    uint256 fee,            // In quote asset as wei
    uint256 spread          // In percentage (1e6 = 100%)
);
```

{% endcode %}

### Deposit liquidity

Deposits are managed via the [vault](https://docs.nekodex.org/nekodex-playground/docs-for-devs/contracts/vault "mention") contract. Only collateral supported by the vault can be used by makers (LPs).

<figure><img src="https://3534130831-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ft9pvATmvHXBuirxBUKA6%2Fuploads%2F1vLaeufQonC9lKdBzfHb%2Fimage.png?alt=media&#x26;token=22418f32-67fb-4ad8-9c54-96d753e5979d" alt=""><figcaption></figcaption></figure>

### Withdraw liquidity

LP holds a share of the global Maker account value, and can withdraw according to the value of their share.

* Example:
  * Maker account value = 100 USDC
    * `accountValue = margin + positionSize * price + openNotional`
    * `price = abs(fillOrder(positionSize) / positionSize)`
  * LP withdraw 10% of liquidity
    * `withdraw(share)`
    * `sharePrice = accountValue / totalSupply()`
  * LP gets 10 USDC (`share * sharePrice`)

## Pricing

### Quote Token

* At launch, all prices are in USD

### Dynamic Premium (spread calculation)

A spread is added to trades to offset makers' risk exposure. Risk exposure potentially includes:

* Long/short skew
* Entry price
* Funding (however, impacts should be minimal because the maker aims to have zero positions over a long period of time)

Risk mitigation:

* Minimize inventory risk (e.g. target position size = 0)
* One-sided circuit breaker when maximum risk exposure is reached (stop taking more long or short, but accept trades that reduce exposure)

Dynamic premium is modeled on a simplified Avellaneda-Stoikov model.

* Givens
  * `midPrice = oraclePrice`: Traditionally in a CLOB, `midPrice` is the midpoint of max bid & min ask; in `PythOracleMaker` we let `oraclePrice` assume this role because you can think of it as the center of bid & ask when spread = 0.
  * `reservationPrice`: Risk-adjusted price for market making. It is a function of `midPrice` (`oraclePrice`), the maker’s risk exposure, and the configurables below. The premium is the difference between `reservationPrice` and `midPrice`
    * `reservationPrice = midPrice * (1 - maxSpread * makerPosition / maxAbsPosition)`
  * `bid = min(midPrice, reservationPrice)`
  * `ask = max(midPrice, reservationPrice)`
* Configurables
  * `maxSpread`: Sensitivity of the maker’s price premium to its risk exposure. This is modeled as the maximum price difference allowed between `midPrice` and `reservationPrice`. The larger `maxSpread` is, the faster the maker increases the premium in response to higher risk exposure.
  * `maxAbsPosition`: Position size cap where the maker would stop taking more positions. We also use it to calculate `reservationPrice`. The max position is a safety threshold and should be configured so that it is not reached too often, because once it is reached, the maker would stop providing liquidity on one side.

## Contracts

{% hint style="info" %}
See [order-gateway](https://docs.nekodex.org/nekodex-playground/docs-for-devs/contracts/order-gateway "mention")for more details about interacting with oracle maker pools.
{% endhint %}

### OracleMaker.sol

#### Event

{% code overflow="wrap" %}

```solidity
event Deposited(
    address depositor,
    uint256 shares, // Amount of shares minted
    uint256 underlying // Amount of underlying token deposited
);
```

{% endcode %}

{% code overflow="wrap" %}

```solidity
event Withdrawn(
    address withdrawer,
    uint256 shares, // Amount of shares burned
    uint256 underlying // Amount of underlying tokens withdrawn
);
```

{% endcode %}

{% code overflow="wrap" %}

```solidity
/// @notice Emitted when an order is filled by a Pyth Oracle Maker.
///         It reveals information associated with the trade price.
event OMOrderFilled(
    uint256 marketId,
    uint256 oraclePrice, // In quote asset as wei, assume price >= 0
    int256 baseAmount, // Base token amount filled (from taker's perspective)
    int256 quoteAmount // Quote token amount filled (from taker's perspective)
);
```

{% endcode %}

#### Public functions (write)

{% code overflow="wrap" %}

```solidity
/// @notice Function is currently whitelisted.
///         Whitelist will be removed at a future date.
function deposit(uint256 amountXCD) external onlyWhitelistLp returns (uint256) {
        address depositor = _sender();
        address maker = address(this);

...
```

{% endcode %}

{% code overflow="wrap" %}

```solidity
/// @notice Function is currently whitelisted.
///         Whitelist will be removed at a future date.
function withdraw(uint256 shares) external onlyWhitelistLp returns (uint256) {
        address withdrawer = _sender();

...
```

{% endcode %}

{% code overflow="wrap" %}

```solidity
/// @notice Function should be called via the order gateway
function fillOrder(
        bool isBaseToQuote,
        bool isExactInput,
        uint256 amount,
        bytes calldata
    ) external onlyClearingHouse returns (uint256, bytes memory) {
        uint256 basePrice = _getPrice();
        uint256 basePriceWithSpread = _getBasePriceWithSpread(basePrice, isBaseToQuote);

        // - `amount` base -> `amount * basePrice` quote
        //   (isBaseToQuote=true, isExactInput=true, openNotional = `amount * basePrice`)
        // - `amount` base <- `amount * basePrice` quote
        //   (isBaseToQuote=false, isExactInput=false, openNotional = -`amount * basePrice`)
        // - `amount / basePrice` base -> `amount` quote
        //   (isBaseToQuote=true, isExactInput=false, openNotional = `amount`)
        // - `amount / basePrice` base <- `amount` quote
        //   (isBaseToQuote=false, isExactInput=true, openNotional = -`amount`)

        int256 baseAmount;
        int256 quoteAmount;
        uint256 oppositeAmount;

...
```

{% endcode %}

#### Public functions (read)

{% code overflow="wrap" %}

```solidity
/// @notice Read current pool utilization ratio
function getUtilRatio() external view returns (uint256, uint256) {
        if (totalSupply() == 0) {
            return (0, 0);
        }
    
        IVault vault = _getVault();
        int256 positionSize = vault.getPositionSize(_getOracleMakerStorage().marketId, address(this));
    
        if (positionSize == 0) {
            return (0, 0);
        }
    
        uint256 price = _getPrice();
        int256 positionRate = _getPositionRate(price);
        // position rate > 0, maker has long position, set long util ratio to 0 so taker tends to long
        // position rate < 0, maker has short position, set short util ratio to 0 so taker tends to short
        return positionRate > 0 ? (uint256(0), positionRate.toUint256()) : ((-positionRate).toUint256(), uint256(0));
    }

/// @param
function getUtilRatio() external view returns (
        uint256 longUtilRatio, 
        uint256 shortUtilRatio
    );
```

{% endcode %}

## Numeric Representation

* Pyth uses [fixed-point numeric representation](https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices#fixed-point-numeric-representation).
* `PythOracleMaker` will translate Pyth price into its own native numeric representation: `wei`
* Assumes `oraclePrice >= 0`

## Run-time Dependencies

* [Pyth EVM contract addresses](https://docs.pyth.network/price-feeds/contract-addresses/evm)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nekodex.org/nekodex-playground/docs-for-devs/contracts/maker/oracle-maker.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
