[MRC-34] Oracle Migration to Pyth

Summary

The objective of this proposal is to migrate from the current oracle implementation (solely based on TWAPs), to an implementation that incorporates Pyth as the main price feed source.

The reason this migration is needed is that, as Osmosis launches concentrated liquidity (CL) functionality, we expect most of the liquidity on the current XYK pools (which are the source of the TWAPs used by Mars) to eventually migrate to CL pools. As this happens, the TWAPs used by Mars could become vulnerable to manipulation attacks given the lower levels of liquidity on the traditional XYK pools. Additionally, it has already been shown on Ethereum that CL pools are not a reliable source for TWAP-based oracles, so simply using a TWAP from the new CL pools wouldn’t be a robust solution.

As such, we believe that the most sensible and robust approach is to incorporate Pyth as the main price feed for Mars. The specific proposed implementation will be explored in more detail below.

Specification

This proposal involves two main changes for Mars:

  1. The first one relates to the asset everything is normalized to in Mars. Currently, that asset is OSMO. However, as Pyth feeds are denominated in USD, the normalization asset in Mars would need to be changed to USD.
  2. The specific oracle implementations for each asset. Note that as not all assets are supported by Pyth, a hybrid implementation (where a TWAP and a Pyth based feed are used) is needed for some.

The following is the proposed new implementation for each asset currently listed on the Red Bank:

Asset Current Implementation Proposed New Implementation
OSMO NA OSMO/USD from Pyth
ATOM ATOM/OSMO TWAP ATOM/USD from Pyth
axlUSDC axlUSDC/OSMO TWAP USDC/USD from Pyth
stATOM stATOM/OSMO = stATOM/ATOM (TWAP) * ATOM/OSMO (TWAP) stATOM/USD = stATOM/ATOM (TWAP) * ATOM/USD (Pyth)
axlWBTC axlWBTC/OSMO TWAP BTC/USD from Pyth
axlWETH axlWETH/OSMO TWAP ETH/USD from Pyth
AXL AXL/OSMO TWAP AXL/USD = AXL/OSMO (TWAP) * OSMO/USD (Pyth)

Some additional notes for specific assets:

  • axlUSDC, axlWETH, axlWBTC: Please notice that the proposed implementation uses the price of the underlying asset, rather than the bridged asset. This is important given that, if anything were to happen to the bridge (such as a loss of assets), Mars would effectively be unaware of it and would still be pricing the bridged assets as the underlying, opening the protocol to potential losses. This is a known but important risk. As such, we think the current implementation should be considered an interim one, as a more robust implementation that incorporates bridge proof of reserves is adopted.
  • stATOM: This hybrid implementation is needed because Pyth doesn’t offer an stATOM/USD feed. Stride is currently working on deploying a redemption rate contract on Osmosis which would allow the current TWAP-based implementation to be migrated to a redemption rate-based one. As soon as the Stride contract is ready, the current implementation could be updated.
  • AXL: This hybrid implementation is needed because Pyth doesn’t offer an AXL/USD feed yet. However, Pyth is currently working to incorporate this feed and as soon as they do, the current implementation could be migrated to Pyth.

Implementation

A new price source (red-bank/price_source.rs at master · mars-protocol/red-bank · GitHub) has to be added to support Pyth.

It contains:

  • contract_addr - Pyth contract address where price feeds are loaded/queried.

  • price_feed_id - price feed id of an asset from the list Price Feed IDs | Pyth Network.

  • max_staleness - the maximum number of seconds since the last price was seen by an oracle, before rejecting the price as too stale.

  • denom_decimals - assets are represented as utokens and every asset can have different decimals (e.g. OSMO - 6 decimals, WETH - 18 decimals).

Our prices will be denominated in uusd (base_denom changes from “uosmo” to “uusd”).

In addition, we need to add the base_denom_decimals field to the config to properly normalize prices for utokens in uusd.

Our Oracle contract queries Pyth contract_addr and based on returned price feed does few calculations to normalize price in uusd.

Pyth price feeds represent numbers in a fixed-point format.

The same exponent is used for both the price and confidence interval.

The integer representation of these values can be computed by multiplying by 10^exponent.

As an example, imagine Pyth reported the following values for ATOM/USD:

expo: -8

conf: 574566

price: 1365133270

The confidence interval is 574566 * 10^(-8) = $0.00574566, and the price is 1365133270 * 10^(-8) = $13.6513327.

Moreover, we have to represent the price for utoken in uusd (instead of token/USD).

Pyth price should be normalized with token decimals so:

ATOM/USD = price * 10^expo

uAtom/uusd = ATOM/USD / 10^atom_decimals * 10^base_denom_decimals = price * 10^expo / 10^atom_decimals * 10^usd_decimals

uAtom/uusd = 1365133270 * 10^(-8) * 10^(-6) * 10^6 = 1365133270 * 10^(-8) = 13.6513327

Copyright

Copyright and related rights waived via CC0.

Disclaimers/Disclosures

This proposal is being made by Delphi Labs Ltd., a British Virgin Islands limited company. Delphi Labs engages in incubation, investment, research and development relevant to multiple ecosystems and protocols, including the Mars Protocol. Delphi Labs and certain of its service providers and equity holders own MARS tokens and have financial interests related to this proposal. Additionally, Delphi Labs is one of several entities associated with one another under the “Delphi Digital” brand. Delphi Digital’s associated entities and/or equityholders or service providers of such entities may hold MARS and may have financial interests related to this proposal. All such entities, service providers, equity holders and other related persons may also have financial interests in complementary or competing projects or ecosystems, entities or tokens, including Osmosis/OSMO. Delphi Ventures is an investor in Pyth. Jump Crypto, which has interests in Pyth, is an investor in Delphi Labs. These statements are intended to disclose relevant facts and to help identify potential conflicts of interest, and should not be misconstrued as a complete description of all relevant interests or conflicts of interests; nor should they be construed as a recommendation to purchase or acquire any token or security.

This proposal is also subject to and qualified by the Mars Disclaimers/Disclosures. Delphi Labs may lack access to all relevant facts or may have failed to give appropriate weighting to available facts. Delphi Labs is not making any representation, warranty or guarantee regarding the accuracy or completeness of the statements herein, and Delphi Labs shall have no liability in the event of losses or damages ensuing from approval or rejection or other handling of the proposal. Each user and voter should undertake their own research and make their own independent interpretation and analysis of all relevant facts and issues to arrive at their own personal determinations of how to vote on the proposal.

1 Like

Whilst I don’t disagree with the change to use Pyth over CL TWAP. Would it not make some sense to include the CL TWAP as a fallback to go along with this work, or even using it to validate the Pyth oracle results rather than abandoning TWAP oracles altogether?

2 Likes

@deif how would you propose your solution to look like?

Some examples of what we could do is take MIN(TWAP, Pyth) which could help mitigate someone manipulating their collateral to the upside. This wouldn’t stop them from manipulating it to the downside however… you could build a more sophisticated algorithm that looks at price deviations between the TWAP and Pyth but the trade off is a lot more complexity in the SC code, and in general it’s good to make smart contracts as dumb as practical.

I think it’s hard to reason about TWAPs on top of concentrated liquidity pools as it’s hard to know what the profile of liquidity is going to be and because of that the cost of manipulation, it can be pretty low.

Pyth has some more sophisticated features that can be utilised in future around price staleness, confidence interval and EMA/Spot divergence. I think these will help mitigate some of the concerns, and it’s likely the TWAP will end up being one of the price feeds for Pyth anyway if there is enough liquidity in the pool - in which case the mentioned metrics will help act as circuit breakers if something is not working correctly or manipulated.

I more meant using the TWAP as a fallback in the case of price staleness. Much like how in Ethereum the protocol Liquity uses Chainlink as the primary feed, and Tellor as a secondary fallback feed in cases where the Chainlink price goes stale.

Admittedly I’m not totally familiar with the way Mars handles stale prices at the moment, so if there are already mechanisms like this in place I’d like to hear it so I can be confident of how the system will perform in edge cases.

Pythnet produces blocks every 1 second and these prices need to be pulled on chain. The Pyth oracle changes delegate this update of price sources to a user interaction within the web app, so everytime the user does a transaction it updates the price feeds as the first msg in that transaction. This means the price should never be stale unless the user is using a third party UI which doesn’t include this price update as part of the user action, or because there is an underlying issue with Pythnet. This comes as a small cost to the user in gas but ensures a decentralised and incentivised way to push updates to the price oracle rather than something like relying on a centralised bot to do the updating. Note other users such as liquidation bots might also choose to push price updates in order to make a position liquidatable.

I think in the case where there is an underlying issue with Pythnet, the protocol would actually want the oracle to hit its staleness check and return an error, the opposite could potentially open up an attack vector. Lets say the attacker runs a DOS on either Pythnet or the server/network that holds the off chain signed price update messages to be retrieved and then pushed on chain. If the protocol is falling back to a concentrated liquidity pool TWAP then the attacker can manipulate the pool, at potentially a relatively low cost if the current concentration at market price is low, to cause collateral to increase in price or debt to decrease in price rapidly even with a 30min TWAP, allowing the attacker to take out more debt than should be allowed and potentially ending up with protocol bad debt as a result.

Wouldn’t the scenario where the Pyth price becomes stale result in the potential for bad debt to occur also? If there are no fallbacks then bad debt can naturally accumulate due to liquidators not being able to remove the positions. In this case what would be the fallback plan?

You’re correct, however I personally think when there is an invalid oracle price it is still better to return an error then fallback to a potentially easily manipulatable oracle source.

Labs has run some simulations in big market down turns, it generally takes around 24 hours for the protocol to accumulate any significant amount of bad debt. So not only would the oracle have to return error, but it would have to do so within extreme market conditions.

In contrast, falling back to an insecure oracle opens the door to severe price manipulation of an asset, where it’s possible to have a much faster and larger price movement and can be orchestrated as part of an attack against the oracle network + TWAP oracle + protocol.

The fallback plan would be to investigate and resolve asap any oracle issues to get the oracle price source operational again. Note that a >24 hour outage of Pythnet and/or Wormhole would quite likely indicate some pretty dire situation within the crypto space where TWAPs might not be our saviour either!

I think these are some good conversations to have in the public regarding the pros/cons of the migration in oracle price sources so thank you for participating

1 Like