Expanding support to other price oracles

Percent launched as the first Compound-style money market using Chainlink price oracles. Chainlink is rapidly adding new feeds, and as it does so Percent can take advantage by adding new markets without changes to its smart contracts.

But some assets that could differentiate Percent from other money markets are not yet supported on Chainlink. Examples include:

  • defi project tokens like BAL, UMA, MTA, NXM, DOUGH.

  • liquidity provider receipts (LP tokens) from automated market makers (AMMs) like Uniswap, Curve, Balancer, or Sushiswap.

  • yield-farming vault receipts from projects like Yearn, Yam, Pickle, etc

  • yield-generating LP baskets like Curve’s yUSD (aka yyDAI+)

  • wrapped versions of non-ETH assets with soft pegs like renBTC, pBTC, sBTC

  • ETF-style baskets, like the DeFi Pulse Index (DPI) on Set Protocol

The motivation for adding most of these would be to expand the range of collateral that users could provide – such assets are expected to have very little borrow demand and are not supplied to earn yield.

Some factors to consider when evaluating an oracle:

  • security – how difficult is it to manipulate the oracle output

  • reliability: liveness and freshness – what events can disrupt or delay the oracle (example: unusually high gas prices)

  • robustness – what market conditions can produce undesired oracle outputs (example: security breach at a centralized exchange causing large price differences across venues)

  • time-sensitivity – latency between real-world market changes and oracle response, smoothing behavior, ability to reject flash crashes and spikes

Here are options we have for oracle data sources:

  1. Internal price feed operated by the project itself, like Compound used until a few months ago.

  2. Open Oracle standard feed from Coinbase and/or OKEx.

  3. Chainlink.

  4. Other Chainlink-like projects, like Band.

  5. Uniswap TWAP.

Thoughts on the tradeoffs between the choices above:

My personal opinion would be to immediately reject #1 (internal price feed), not least because of its undesirable security profile: insiders have the power to manipulate prices, and hackers could compromise the system to do the same.

Compound is currently using #2 (Open Oracle standard), but accepts only prices from Coinbase. This limits their choice of assets to those with prices published by Coinbase. Other disadvantages of these feeds is that they reflect the price on a single exchange and depend on the operational status of a single service that may fail internally or be the victim of a DDOS attack. More info here: https://blog.coinbase.com/introducing-the-coinbase-price-oracle-6d1ee22c7068.

It would be a bit messy to mix any Open Oracle sources with our existing Chainlink sources because the data flows are so different. In the Chainlink case, any operation that needs a price consults the Chainlink oracle in-band and automatically gets the latest price. In the Open Oracle case, anybody can post a recent price to the on-chain oracle, and then any operation that needs a price consults that and gets the latest value posted, whatever it was. Although both methods can co-exist for different assets, it adds complexity to user understanding and ecosystem tools like liquidators.

Uniswap TWAP is available for any asset that is traded on Uniswap (pretty much everything). Their design builds in a time-averaging mechanism that lets you calculate a robust average price over any time period. Manipulating the price effectively means manipulating the global market price, since arbitrageurs will pull the global price and Uniswap local price together during the interval of the measurement. The cost of manipulation scales with the global liquidity of the asset.
More info: https://uniswap.org/docs/v2/core-concepts/oracles and https://uniswap.org/audit.html#org87c8b91

My opinion is that just two oracle sources – Chainlink and Uniswap TWAP – would be sufficient for any asset that we are interested in supporting. Adding Uniswap TWAP would require us to write another adapter, like we have for Chainlink, with security review and audit, but the amount of code needed is quite small and fairly simple. Here is an example implementation from the Uniswap team in about 60 lines of code: https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/examples/ExampleOracleSimple.sol

@pyggie this is a great summary, thank you!

So would you view using TWAPs for an asset that has little liquidity on Uniswap relative to its total float as a vulnerability?

How could we go about creating a framework to properly assess such risks? Would the use of low enough collateral factor combined with high enough liquidation threshold mitigate them, in your view?

Surely there should be a way to avoid the main risk of a token’s price getting artificially inflated, but I am not familiar enough with Solidity to know what that would be. A naive example is keeping track of the daily median price of an asset, and then capping today’s price to 2x of yesterday’s median.

Other than that tokens would have to be handpicked to avoid simple stuff like infinite mint capability and to ensure there is enough liquidity on Uniswap. We would have to constantly review this and remove assets with low liquidity. It just seems that there should be an extra automatic backstop, another naive example is for instance all depositors of PCT can only borrow up to $1M in other assets combined.

Keep in mind we only have one such setting in our disposal, collateralFactor in Compound-speak is the same thing as liquidation threshold. We can only set a lower collateral factor (a la Aave) in the UI, which can be ignored by people borrowing on the smart contract itself.

Wasn’t aware of it, thank you! Shouldn’t setting a high enough liq threshold mitigate this on the contract level?

Not sure I follow, what would you mitigate with a high liquidation threshold? What I was saying is if we put in a collateralFactor of e.g. 75%, people can borrow 75% using the smart contract, and then if the price moves against them the slightest bit they can be liquidated.

Ok, so actually the liquidation threshold is enforced by collateralFactor on the smart contract level, right?

sorry if i sound a bit confused, as i am and obviously haven’t read the contract to understand how it works under the hood.

Yes, the liquidation threshold is the flipside of the coin. Each determines the other.

Collateral factor = max allowed value of (borrowed value) / (supplied value)

Liquidation threshold = min allowed value of (supplied value) / (borrowed value)

So a collateral factor of 75% has a liquidation threshold of 133%. The Percent (and Compound) smart contract allows you to borrow all the way to this limit. The UI could set a more conservative limit, but it would not apply to anyone bypassing the UI and using the smart contracts directly.

Some protocols (e.g. Aave and dYdX I think), set two different limits, one for when opening the position (“initial margin”) and the other after it has been established and prices move (“maintenance margin”).

@pyggie thank you! i guess i have to read more protocol documentation :grimacing: