(wip) Towards better price oracle schemes, Part II. Practical price manipulation
RSS GANG THIS ARTICLE ISNT READY
Price oracles are a crucial necessity, but also a great weakness for DeFi. Most DeFi projects use centralized oracles with no fallback, which means that they are a single point of failure. In this post, I will do a deep dive into oracles, especially decentralized ones, and provide multiple potential solutions to problems facing protocols that use them.
This is part II in a II-part series on oracles. This part talks about how various oracle solutions can be attacked and manipulated. It covers both centralized and decentralized oracle solutions.
Note: This article is not designed to FUD anything. It collects facts about various oracle solutions and how they can be manipulated, so more people are aware of the risks of using them. Remember, only the paranoid survive.
TLDR
If you can’t be bothered to read the whole blog post, which is fine, here are some important takeaways:
- An attack is defined as manipulating an oracle price to cause harm to a protocol or its users.
- Centralized oracles aren’t invincible.
Defining an attack
Before diving any deeper, we need to lay some groundwork and define what the word attack means in this context. We define attacking an oracle as manipulating the reported price of a pair so it deviates enough from the traded market price where it can cause unforseen losses to a protocol and its users.
As an example, lets say that there is a centralized oracle reporting the price of an asset pair. If the centralized oracle has not updated for a long time, and the current market price has deviated from the last reported price, we would classify that as an attack. If the oracles last reported price deviates from the fair market price by a large margin, we would also classify that as an attack. However, if the deviation in price reported by an oracle is negligable, we would not classify that as an attack. For example, Chainlink has a deviation threshold where it will not publish new prices unless the asset has moved enough.
Attacks against centralized oracles
We define centralized oracles as any oracle that gathers prices off-chain, and then posts said data on-chain. This includes Chainlink, Band, DIA, MakerDAO, amongst others.
Centralized oracles are often held in high regard as generally being the safest, most secure, way to get price data. It is important to keep in mind that these types of oracles rely on external infrastructure to work. This means that there must be a centralized group of people that can, at the very least, create liveness issues or even set arbitrary prices. We’ve covered how Chainlink sidesteps this issue in part I, but it still doesnt solve the core problem. For example, if all node operators were subpoenad, Chainlink would completely stop working as intended.
While these scenarios are very unlikely, they’re important to keep in mind. When designing DeFi protocols, you are writing code that will potentially be used forever. Having your DeFi protocol rely on an externally provided service, is a good way to ensure that wont be happening.
Attacks against optimistic oracles
Optimistic oracles are a very unique breed. We define an optimistic oracle as a system, where one party posts data on chain that can be verifiably proven to be correct/incorrect at a later date. Finality is also mandatory for such a system. This includes Tellor and UMA.
The security model of optimistic oracles revloves around finality. Data should only be considered as true after finality. Due to how the 2 other types of oracles work, this is a very common pitfall when implementing optimistic oracles. We’ve actually seen at least one protocol get exploited by not implementing finality checks properly. This does not mean that optimisitic oracles are necessarily unsafe, rathar that they have an implementation quirk you should keep in mind when designing your protocol. Lets look at how UMA and tellor handle finality so we can see how we can exploit it:
UMA
Tellor
Attacks against on-chain oracles
This is where the real fun begins and probably why you’re here in the first place. We define a on-chain oracle as a system that only uses on-chain market data to querry the price of an asset pair. This includes the Rainshower oracle(shameless plug), as well as ny other way of accessing DEX market data.
In order to describe potential attack vectors, we must classify on-chain oracles. We define them as:
- Primitive
- Time weighted average price (TWAP)
Primitive oracles simply querry a pool for liquidity and calculate the price according to a curve. These types of oracles are very exploitable and are not used at all in any major capacity. TWAP oracles are the way to querry an assets price on-chain. With Uniswap V3, pool contracts can be used to get the liquidity at an average tick and querry the price from there.
1) What
Note: Feel free to skip this if you know how UniV3 works. This is also relevant for V4, but different hooks might change things.
If the last sentance you read makes absolutely no sense, I dont blame you! In order to properly understand how you’d manipulate an Uniswap pools price to your gain, we need to understand how V3 pools work.
Unlike V2, V3 doesnt oppearate on a curve. Instead liquidity providers(LPs) provide liquidity on a tick range. A tick is a small % change in the price of a pair. Therefor a tick range represents a % range in the price of an asset. Take a look at the following diagram:
Here, each color is a representation of a LP position and each bar a tick. We can think of each tick as a mini pool within a pool. Once liquidity in a tick is depleted, we move onto the next tick. If we’re swapping USDC for ETH, we’d be moving up; and vice versa. Not how there are areas that have no liquidity at all. If theres no liquidity remaining at the next tick when swapping, the swap will either fail or incur a lot of slippage.
When performing a swap for an asset pair, the pool will note the current tick for the first swap in a block(This is important and makes our job of manipulationg prices slightly more difficult). We can then use this to get the liquidity for a tick, and by proxy, price.