Oracles

As of now, the developed oracle adapters are Chainlink and Dia oracle adapters. Both adapters include an ERC4626 base and quote vault converter. So if the market token is an ERC4626, you can easily convert this oracles underlying to the overlying ERC4626 price (exclusive of fees while minting and burning such ERC4626 token).

Another type of oracle exist allowing you to combine multiple Mangrove vaults oracle together. This can be used in case an oracle needs both chainlink and dia for example.

Querying an oracle

Here is how to query an oracle. The price adjustement is based on the WETH/USDC market (12 decimals shift).

oracle-query.ts
// Get the oracle price
import { type Address, parseAbi, createPublicClient } from "viem";
import { priceFromTick } from "@mangrovedao/mgv";

const client = createPublicClient(...);
const oracle: Address = "0x...";

const abi = parseAbi([
    "function tick() external view returns (int256)"
])

const tick = await client.readContract({
    address: oracle,
    abi,
    functionName: "tick"
})

const rawPrice = priceFromTick(tick); // 1.0001 ** Number(tick)
const adjustedPrice = rawPrice * 1e12 // adjust for decimals

Further demonstration will not include javascript snippets, but the flow would be more or less the one recommended by the viem documentation.

Addresses of factories can be found in Deployment adresses.

Find the addresses for the chainlink price feeds here.

Choosing the parameters

The parameters to provide are the feed addresses (2 base feed, and 2 quote feed at most allowing feed chaining). One base and one quote ERC4626 vaults can also be provided as well as an optional salt.

https://github.com/mangrovedao/mangrove-vault/blob/899abdb187f1801ba44621c0e25a697edda859e7/src/oracles/chainlink/v2/MangroveChainlinkOracleV2.sol#L40-L46
 * @notice An oracle adapter for Mangrove that supports up to 4 Chainlink price feeds and 2 ERC4626 vault feeds
 * @dev This contract combines up to 4 Chainlink price feeds and 2 ERC4626 vault feeds to create a single price oracle for Mangrove.
 *      For Chainlink feeds, it supports up to 2 feeds to combine for the base price, plus two inverse feeds for the quote price.
 *      For example, if we have A/B, B/C, D/C, and E/D feeds, we can combine them into A/E.
 *      For ERC4626 vaults, it supports one vault feed for the base and one for the quote.
 *      For example, if we have a vault vA containing A tokens (vA/A feed) and a vault vE containing E tokens (vE/E feed),
 *      we can compose them into a vA/vE feed with the above composition.

In order to chain feeds together, we need to specify the token decimals for each feed token. If the token does not exist, assuming 18 decimals is recommended.

For reference, ETH/USD feed, ETH is 18 decmals, and USD does not exist so we take 18 decimals (unless we assume USD to be USDC or USDT in which cas we choose 6). For USDC/USD, we will take 6 for USDC, and 18 for USD. Here is an example of a valid deployment on base for WETH/USDC market.

Checking if the oracle is already deployed

These oracles are deployed with CREATE2 allowing to share an oracle easily by finding vaults with same parameters. All values are immutable ensuring no changes have been done since deployment.

https://github.com/mangrovedao/mangrove-vault/blob/899abdb187f1801ba44621c0e25a697edda859e7/src/oracles/chainlink/v2/MangroveChainlinkOracleFactoryV2.sol#L18-L37
  /**
   * @notice Computes the address of a MangroveChainlinkOracleV2 before it is deployed
   * @param baseFeed1 ChainlinkFeed struct for the first base feed
   * @param baseFeed2 ChainlinkFeed struct for the second base feed
   * @param quoteFeed1 ChainlinkFeed struct for the first quote feed
   * @param quoteFeed2 ChainlinkFeed struct for the second quote feed
   * @param baseVault ERC4626Feed struct for the base vault
   * @param quoteVault ERC4626Feed struct for the quote vault
   * @param salt Unique value for deterministic address generation
   * @return The address where the oracle contract will be deployed
   */
  function computeOracleAddress(
    ChainlinkFeed calldata baseFeed1,
    ChainlinkFeed calldata baseFeed2,
    ChainlinkFeed calldata quoteFeed1,
    ChainlinkFeed calldata quoteFeed2,
    ERC4626Feed calldata baseVault,
    ERC4626Feed calldata quoteVault,
    bytes32 salt
  ) public view returns (address) {

Then to check if it has been deployed, we just need to check the code size.

Deploying the oracle

To deploy the oracle we pass the very same parameters to the factory:

Deploying a Dia oracle adapter

Dia oracles are only limited to DiaOracleV2

Up to 4 feeds (2 base and 2 quote) in addition to one base and one quote vault can be combined.

Choosing the parameters

In order to choose the parameter, do the same as Choosing the parameters. But you also have to provide a string key. The string key then has to be packed encoded. The maximum size of the encoding is now limited to 32 bytes for efficiency.

// Getting the key
import {encodePacked} from "viem";

const key = "ETH/USD";
const encodedKey = encodePacked(["string"], key);
// the byte length of this encoded key must be 32 bytes or less.

Checking if the oracle is already deployed

https://github.com/mangrovedao/mangrove-vault/blob/899abdb187f1801ba44621c0e25a697edda859e7/src/oracles/dia/MangroveDiaOracleFactory.sol#L29-L37
  function computeOracleAddress(
    DiaFeed calldata baseFeed1,
    DiaFeed calldata baseFeed2,
    DiaFeed calldata quoteFeed1,
    DiaFeed calldata quoteFeed2,
    ERC4626Feed calldata baseVault,
    ERC4626Feed calldata quoteVault,
    bytes32 salt
  ) public view returns (address) {

Check the returned address code size to check if it is already deployed.

Deploying the oracle

https://github.com/mangrovedao/mangrove-vault/blob/899abdb187f1801ba44621c0e25a697edda859e7/src/oracles/dia/MangroveDiaOracleFactory.sol#L57-L65
  function create(
    DiaFeed calldata baseFeed1,
    DiaFeed calldata baseFeed2,
    DiaFeed calldata quoteFeed1,
    DiaFeed calldata quoteFeed2,
    ERC4626Feed calldata baseVault,
    ERC4626Feed calldata quoteVault,
    bytes32 salt
  ) external returns (MangroveDiaOracle oracle) {

Combining oracles together

An oracle combiner can combine up to 4 Mangrove compatible oracles together.

Checking existing oracle

https://github.com/mangrovedao/mangrove-vault/blob/899abdb187f1801ba44621c0e25a697edda859e7/src/oracles/OracleCombinerFactory.sol#L29-L32
  function computeOracleAddress(address _oracle1, address _oracle2, address _oracle3, address _oracle4, bytes32 _salt)
    public
    view
    returns (address)

Deploying the oracle

Last updated