๐Ÿ”ฎPrice Oracles

Each CronFi Time Weighted Average Market Maker (TWAMM) pool features a price oracle. The price oracle resembles the same design released in Uniswap V2 (see: whitepaper), however, it has been adapted to support the tracking of virtual order price paths.

The price oracle presents the price of pool asset 0 in terms of asset 1 per asset 0 and vice-versa for the price of pool asset 1. The prices presented are in units of priceยทs, where s=seconds. Multiplying prices by elapsed seconds allows a difference to be taken between two different moments in time to determine the average price of asset 0 and asset 1 on the given time interval.

There are two methods for getting the price oracle data:

  1. getPriceOracle gets the oracle price data as of the Last Virtual Order Block (LVOB), the block in which virtual multi-block orders were last executed and the pool state was updated. The LVOB can be obtained by calling getLastVirtualOrderBlock.

  2. getVirtualPriceOracle gets the oracle price as of the specified block number. The specified block number must be greater than the LVOB and less than or equal to the current block number. The difference between this method and getPriceOracle is that this method projects the price information to the current block, beyond the LVOB, integrating information from the virtual price path. If the LVOB is equal to the current block, getVirtualPriceOracle and getPriceOracle return the same values.

The data returned from both methods for getting price oracle data is:

  1. timestamp: the timestamp in seconds when the data was computed

  2. token0: the price of Token 0 in terms of Token 1 per Token 0, multiplied by total elapsed time, in units of priceยทs, where s=seconds.

  3. token1: the price of Token 1 in terms of Token 0 per Token 1, multiplied by total elapsed time, in units of priceยทs, where s=seconds. The getVirtualPriceOracle method also returns a fourth value, blockNumber. This value is the block number to which the data was computed and will differ from the specified block number if it is less than or equal to the LVOB or greater than the current block number.

Virtual Price Paths

In a conventional Automated Market Maker (AMM) like Uniswap V2, each order occurs in a single block, whereas an order in a CronFi TWAMM can occur in a single block or over many blocks. Multi-block orders are not updated and computed until action takes place on the pool requiring asset accounting in the pool to be updated to the current block. If many blocks have elapsed since an update but multi-block orders are present, the price of assets in the pool may have changed over the period of inactivity. The CronFi TWAMM pool supports these price changes by updating the price oracle values after each block interval where prices are updated. For example, if three block intervals of inactivity have elapsed with multi-block orders present, the CronFi pool upon the next update would modify the oracle price at least three times after virtual orders are executed for each interval. In this way, the CronFi TWAMM presents a more accurate sense of the price of assets in the pool by following the price path of pool assets through periods of inactivity.

Vulnerabilities

Similar to other price oracles, the price oracle of the CronFi TWAMM pools is subject to price manipulation. Their accuracy may be insufficient during periods of extreme price movement. Further research into this matter is left to the reader.

How to use

Code Examples: Average Price of Pool Asset 0

// At block 1000:
//
(uint256 timeStamp_B1000, uint256 token0_B1000, uint256 token1_B1000) = getPriceOracle();


// Multiple multi-block virtual orders are active, the last virtual order
// update happened at block 2000.
// The blockchain is currently at block number 3000:
//
uint256 LVOB = getLastVirtualOrderBlock();ย ย // Returns 2000
(uint256 timeStamp_B2000, uint256 token0_B2000, uint256 token1_B2000) = getPriceOracle();

(uint256 timeStamp_B3000, uint256 token0_B3000, uint256 token1_B3000, uint256 blockNumber3000) = getVirtualPriceOracle(block.number);
require(blockNumber3000 == block.number, "Virtual Price Oracle returned result for block number different than specified.");


// Now compute the average price of Pool Asset 0 at Block 2000, relative to Block 1000:
//
//ย ย ย NOTE: the average computed below has 112 fractional bits and 144 bits above the decimal place.
//
uint256 avgPriceToken0_B2000 = (token0_B2000 - token0_B1000) / (timeStamp_B2000 - timeStamp_B1000);


// Now compute the average price of Pool Asset 0 at Block 3000, relative to Block 1000 using the
// data from the virtual price oracle:
//
//ย ย ย NOTE: the average computed below has 112 fractional bits and 144 bits above the decimal place.
//
uint256 avgPriceToken0_B3000 = (token0_B3000 - token0_B1000) / (timeStamp_B3000 - timeStamp_B1000);


// Alternately get the average price of token 0 over a shorter time-interval at Block 3000 (relative to
// Block 2000):
//
//ย ย ย NOTE: the average computed below has 112 fractional bits and 144 bits above the decimal place.
//
uint256 avgPriceToken0_B3000SI = (token0_B3000 - token0_B2000) / (timeStamp_B3000 - timeStamp_B2000);
</code>

Code Examples: Average Price of Pool Asset 1

To compute the average price of pool asset 1, the arithmetic operations are similar to those above, for example, using the values gathered above:

// Now compute the average price of Pool Asset 1 at Block 3000, relative to Block 1000 using the
// data from the virtual price oracle:
//
//ย ย ย NOTE: the average computed below has 112 fractional bits and 144 bits above the decimal place.
//
uint256 avgPriceToken1_B3000 = (token1_B3000 - token1_B1000) / (timeStamp_B3000 - timeStamp_B1000);

Who needs this?

Native Price Oracles

In our discussions with Olympus DAO & MakerDAO, the protocols cited a need for a TWAMM pool native oracle system to track asset prices. This is a huge pain point that Maker has been exploring a solution for because theyโ€™re unable to onboard small-cap tokens with insufficient DAI liquidity pairing. See the myriad of tokens requesting onboarding every month to the MakerDAO platform:

Last updated