Price Oracle Manipulation

Extropy.IO
23 min readSep 9, 2021

In this article we look at type of economic attack on DeFi projects in the form of manipulation of price oracles.

What Are Price Oracles

On Ethereum, where everything is a smart contract, so too are price oracles. As such, it’s more useful to distinguish between how the price oracle gets its price information. In one approach, you can simply take the existing off-chain price data from price APIs or exchanges and bring it on-chain. In the other, you can calculate the instantaneous price by consulting on-chain decentralized exchanges.

Both options have their respective advantages and disadvantages. Off-chain data is generally slower to react to volatility, which may be good or bad depending on what you’re trying to use it for. It typically requires a handful of privileged users to push the data on-chain though, so you have to trust that they won’t turn evil and can’t be coerced into pushing bad updates. On-chain data doesn’t require any privileged access and is always up-to-date, but this means that it’s easily manipulated by attackers.

A further distinction can be made between off-chain and on-chain oracles, that is, whether they are centralized or decentralized.

Off-chain Centralized Oracle

This type of oracle simply accepts new prices from an off-chain source, typically an account controlled by the project. Due to the need to quickly update the oracle with new exchange rates, the account is typically an EOA and not a multisig. There may be some sanity checking to ensure that prices don’t fluctuate too wildly. Compound Finance and Synthetix mainly use this type of oracle for most assets

Off-chain Decentralized Oracle

This type of oracle accepts new prices from multiple off-chain sources and merges the values through a mathematical function, such as an average. In this model, a multisig wallet is typically used to manage the list of authorized sources. Maker and Chainlink use this type of oracle for ETH and other assets.

On-chain Centralized Oracle

This type of oracle determines the price of assets using an on-chain source, such as a DEX. However, only a central authority can trigger the oracle to read from the on-chain source. Like an off-chain centralized oracle, this type of oracle requires rapid updates and as such the triggering account is likely an EOA and not a multisig. dYdX and Nuo use this type of oracle for certain assets

On-chain Decentralized Oracle

This type of oracle determines the price of assets using an on-chain source, but can be updated by anyone. There may be some sanity checking to ensure that prices don’t fluctuate too wildly. DDEX uses this type oracle for DAI, while bZx uses this type of oracle for all assets.

“Overcollateralized Loan” Pattern (DeFi primitive). AKA the “nonrecourse loan”.

This is a very common DeFi primitive used in all kinds of projects: Maker/DAI, Compound, Synthetix, etc. It’s used for decentralized loans, derivatives projects, stable coins, and more. It may not be obvious because of different/custom terminology use, e.g.:

  • Collateralization ratio
  • Collateralized Debt Position (CDP)
  • Liquidation discount
  • Stability fee
  • Collateral factor

It is a useful, flexible, and powerful pattern, and it has become quite common. It’s worth learning about if you aren’t already familiar with it.

Before we can talk about decentralized lending however, let’s introduce traditional lending. When you take out a loan, you typically need to provide some sort of collateral so that if you default on your loan, the lender can then seize the collateral.

Loans come in two flavors: Unsecured loans and secured loans.

  • Secured Loans: Loans where the borrower is required to put down some amount of collateral. For example: mortgage loans, trading stocks on margin, pawn shop loans, car loans.
  • Collateral: Any asset owned by the borrower used to secure the loan. If the borrower defaults, the collateral and use it to cover any unpaid principal and interest. There may be an additional penalty to the borrower if this has to occur.

Loan-to-Value (LTV) Ratio is the amount the borrower owes the lender (principal + interest) divided by the value of the collateral. Higher LTV ratio is riskier for the lender, i.e. the lower the collateral the higher the LTV.

There are two types of secured loans: Recourse loans and nonrecourse loans. Difference is what recourse the lender has if the borrower defaults and the collateral is not enough to cover the loan. I.e. with nonrecourse loans the lender does NOT have any means of pursuing the borrower for the remainder owed.

Almost all loans in the open blockchain space are nonrecourse loans, therefore the lender wants to see very low LTV ratio (typically 50–60% or lower). For example, a 50% means that if you wanted to borrow $100 you’d have to deposit $200 ($100 principal + $100 collateral).

In DeFi, smart contracts calculate the LTV value using the following LTV formula:

$$ ((# of borrowed tokens) * (borrow token price)) / ((# of collateral tokens) * (collateral token price)) $$

The following table compares the factors responsible for increasing or decreasing the TVL:

What increases the LTV ratio? What decreases the LTV ratio?

Time (due to interest).

I.e. outstanding borrowed tokens accrue interest over time

N/A Borrowing more tokens Paying back some borrowed tokens Removing some collateral Adding more collateral tokens Price of borrowed token increases Price of borrowed tokens decreases Price of collateral token decreases Price of collateral tokens increases

Basically, the TVL increases when the numerator part of the fraction grows proportionally larger than the denominator (i.e. borrowed tokens value > collateral tokens value) and decreases when the opposite happens (i.e. collateral tokens value > borrowed tokens value).

Security considerations

In decentralized lending, the same process occurs as in the “real world” except now the lender is a smart contract that is isolated from the outside world. This means that it can’t simply “know” the fair market value (FMV) of whatever collateral you’re trying to provide, i.e. the FMV of the market value of the token borrowed and the collateral token.

Getting Fair Market Value of Borrow and Collateral Tokens

1. Manipulated Token’s Price Risk

This represents the most common scenario in DeFi hacks, most often when protocols rely on spot price directly from DEXs.

If an attacker can control the price oracle, they can steal all the money. The process is : * Simply adjust the borrow token price up (or the collateral price down), which puts borrower in default, * Liquidate the collateral for approximately $0. This is usually the case for on-chain centralized oracles, ie. DEXs, whereby an attacker can use flashloans to manipulate the price of AMMs in order to change the spot price of a token just before the lender smart contract looks it up.

Alternatively, if they can censor or DoS the price oracle they can they can steal money although with less of a discount. This happens in off-chain centralized oracles.

Example scenario — Imagine you wanted to bring decentralized lending to the blockchain.

Users are allowed to deposit assets as collateral and borrow other assets up to a certain amount determined by the value of the assets they’ve deposited. Let’s assume that * a user wants to borrow USD using ETH as collateral, * the current price of ETH is 4000 USD, * the LTV ratio is 66.6% (i.e. a collateralization ratio of 150%).

If the user deposits 3750 ETH, they’ll have deposited 1,500,000 USD of collateral. They can borrow 1 USD for every 1.5 USD of collateral, so they’ll be able to borrow a maximum 1,000,000 USD from the system.

But of course, on the blockchain it’s not as simple as simply declaring that 1 ETH is worth 4000 USD because a malicious user could simply declare that 1 ETH is worth 10,000 USD and then take all the money from the system. As such, it’s tempting for developers to reach for the nearest price oracle shaped interface, such as the current spot price on Uniswap, Kyber, or another decentralized exchange.

At first glance, this appears to be the correct thing to do. After all, Uniswap prices are always roughly correct whenever you want to buy or sell ETH as any deviations are quickly corrected by arbitrageurs. However, as it turns out, the spot price on a decentralized exchange may be wildly incorrect during a transaction as shown in the example below.

Consider how a Uniswap reserve functions. The price is calculated based on the amount of assets held by the reserve, but the assets held by the reserve changes as users trade between ETH and USD. What if a malicious user performs a trade before and after taking a loan from your platform?

  • Before the user takes out a loan, they buy 5,000 ETH for 2,000,000 USD.
  • The Uniswap exchange now calculates the price to be 1 ETH = 1,733.33 USD.
  • Now, their 375 ETH can act as collateral for up to 433,333.33 USD worth of assets, which they borrow.
  • Finally, they trade back the 5,000 ETH for their original 2,000,000 USD, which resets the price.

The net result is that your loan platform just allowed the user to borrow an additional 333,333.33 USD without putting up any collateral.

This case study illustrates the most common mistake when using a decentralized exchange as a price oracle — an attacker has almost full control over the price during a transaction and trying to read that price accurately is like reading the weight on a scale before it’s finished settling. You’ll probably get the wrong number and depending on the situation it might cost you a lot of money.

2. Centralization Risk

Should a private key from a privileged account get compromised, the attacker could simply mint collateral token can steal all funds. (Borrow against the new, diluted collateral tokens before the oracle can reflect the new price).

3. Arbitrage Risk

The latest reported price is, on average, at least several seconds behind the current “public knowledge” price, which can open up arbitrage opportunities.

In Chainlink, oracles only update every 20 minutes or if the price changes over 0.5%.

4. Speed VS. Security

Currently, there isn’t really a known decentralized method of getting trustworthy price info on-chain in a way that is both fast and prohibitively expensive to manipulate. For example. Augur is decentralized and prohibitively expensive to manipulate, but too slow for price updates. Chainlink/Provable are fast, but cheap to manipulate (simply attack the websites they query). Therefore, the median of multiple trusted third parties is the de facto “standard”.

Additionally, no two projects using this pattern should ever share a price oracle (of the “median of trusted parties” variety); this increases the benefit of exploiting the price oracle without increasing the cost of doing so, reducing economic security.

Calculating Interest on Borrowed Tokens

For an advanced lecture on this topic, see Computing and Accumulating Interest On-chain.

1. Fixed-Point Libraries

Calculating interest requires a fixed-point library. Most projects seem to be rolling their own.

2. Truncation Issues (Division)

Both when withdrawing and depositing in/from a DeFi protocol, there is a rounding error for defect, due to the division operation that zeros out 1 wei. For example if 1 wei is worth 1000 DAI (wei) (not 100018), then 999 buys nothing because it is less than the minimum ETH unit wei, ie. 999/1000=0.
To give an example, open your terminal and run the following command:

echo 999/1000 | bc

The effect may be exacerbated if the conversion happened between USDT and ETH, as USDT has 6 decimal places an not 18, a non trivial amount. If you multiply that for every investment, all these pennies accumulate and eventually get swiped up whenever the pool gets emptied.

This becomes a serious issue when division is not done last when making internal calculations, the effect is much bigger; always divide last in solidity! This can result in truncating away interest owed by borrower over short time frames. Can result in “interest free loans” for certain classes of attackers.

3. Measuring Time

Computing interest requires measuring time, but how? One way is to use block.timestamp which can be manipulated by miners but the risk is not that great. Using block.number is worse as it requires assumptions about block time, there is a difficulty bomb, and ETH2.0 block times are unknown.

Division by Zero in TVL

When all collateral has been removed, denominator of LTV Ratio is zero. Must be handled correctly. This can occur when liquidating during (or very near) the “underwater” state, it can also occur when borrower pulls out remaining collateral after paying back entirety of the loan, or if a malicious/compromised/buggy price oracle reports the collateral token price as zero.

Real World Examples

Synthetix sKRW Oracle Malfunction

Synthetix is a derivatives platform which allows users to be exposed to assets such as other currencies. To facilitate this, Synthetix (at the time) relied on a custom off-chain price feed implementation wherein an aggregate price calculated from a secret set of price feeds was posted on-chain at a fixed interval. These prices then allowed users to take long or short positions against supported assets.

On June 25, 2019, one of the price feeds that Synthetix relied on mis-reported the price of the Korean Won to be 1000x higher than the true rate. Due to additional errors elsewhere in the price oracle system, this price was accepted by the system and posted on-chain, where a trading bot quickly traded in and out of the sKRW market. In total, the bot was able to earn a profit of over 1B USD, although the Synthetix team was able to negotiate with the trader to return the funds in exchange for a bug bounty.

Synthetix correctly implemented the oracle contract and pulled prices from multiple sources in order to prevent traders from predicting price changes before they were published on-chain. However, an isolated case of one upstream price feed malfunctioning resulted in a devastating attack. This illustrates the risk of using a price oracle which uses off-chain data: you don’t know how the price is calculated, so your system must be carefully designed such that all potential failure modes are handled properly.

Synthetix MKR Manipulation

In December 2019, Synthetix suffered another attack as a result of price oracle manipulation. What’s notable about this one is that it crossed the barrier between on-chain price data and off-chain price data.

Reddit user u/MusaTheRedGuard observed that an attacker was making some very suspicious trades against sMKR and iMKR (inverse MKR). The attacker first purchased a long position on MKR by buying sMKR, then purchased large quantities of MKR from the Uniswap ETH/MKR pair. After waiting a while, the attacker sold their sMKR for iMKR and sold their MKR back to Uniswap. They then repeated this process.

Behind the scenes, the attacker’s trades through Uniswap allowed them to move the price of MKR on Synthetix at will. This was likely because the off-chain price feed that Synthetix relied on was in fact relying on the on-chain price of MKR, and there wasn’t enough liquidity for arbitrageurs to reset the market back to optimal conditions.

This incident illustrates the fact that even if you think you’re using off-chain price data, you may still actually be using on-chain price data and you may still be exposed to the intricacies involved with using that data.

bZx Second Hack

By relying on an on-chain decentralized price oracle without validating the rates returned, DDEX and bZx were susceptible to atomic price manipulation. This would have resulted in the loss of liquid ETH in the ETH/DAI market for DDEX, and loss of all liquid funds in bZx. Fortunately, no funds were actually lost.

In February 2020, bZx was hacked twice over the span of several days for approximately 1MM USD. You can find an excellent technical analysis of both hacks written by palkeo here, but we will only be looking at the second hack.

In the second hack, the attacker first purchased nearly all of the sUSD on Kyber using ETH. Then, the attacker purchased a second batch of sUSD from Synthetix itself and deposited it on bZx. Using the sUSD as collateral, the attacker borrowed the maximum amount of ETH they were allowed to. They then sold back the sUSD to Kyber.

If you’ve been paying attention, you’ll recognize this as essentially the same undercollateralized loan attack, but using a different collateral and a different decentralized exchange.

Let’s now look at the second transaction 0x7628…, which happened on Tuesday 18th February 2020 at 03:13:58 UTC.

It caused the same effect as the first one, namely opening an under-collateralized position on bZx. However it uses a completely different method, and is more straightforward to understand.

High-level overview

Here are the main calls that happened during the attack:

  1. The attacker borrows 7500 ETH from bZx (flash borrow)
  2. The attacker repeatedly calls Kyber to convert 900 ETH to 155,994 sUSD (distorting the Kyber sUSD prices)
  3. The attacker uses the Synthetix depot contract to convert 3518 ETH to 943,837 sUSD
  4. The attacker borrows 6796 ETH on bZx, sending only 1,099,841 sUSD (oracle attack)
  5. The attacker transfers back 7500 ETH to bZx to repay their flash loan

At the end the attacker ends up with 2378 ETH in their attack contract. They transfer it to their EOA shortly after.

Walkthrough of the transaction

A. The bZx flash borrow

This step is comparable with the first step of the first exploit. Only it uses bZx instead of DyDx.

Again, the goal is to borrow enough money to be able to pull off the exploit, and again, the rest of the attacker activity happen inside of a callback to the attacker contract, initiated by bZx.

B. Distorting the Kyber prices

Kyber uses “reserves”, which provides liquidity. For the ETH-sUSD pair there are two reserves:

  • Uniswap.
  • A Synthetix one, that implements a LiquidityConversionRates that automatically adjusts the price (conceptually similar to Uniswap).

A trade will necessarily either hit one of them (depending on which one gives the best price).

The attacker contract buys most of the sUSD liquidity available on both reserves. For that, they do 19 successive buys:

You can see each trade getting a worse price. That’s the attacker skewing the prices by eating all available liquidity.

We get from 270 ETH/sUSD (normal rate) to a price of 111 ETH/sUSD in Kyber.

C. Buying a lot of sUSD at a normal rate

This is done using the Synthetix Depot contract which has a lot of liquidity that you can access.

The attacker calls the exchangeEtherForSynths() function to exchange 6000 ETH to 943,837 sUSD.

The rate is 157 ETH/sUSD. It’s a bad rate, but significantly better than the distorted rate that Kyber now returns (see above).

D. Borrowing ETH for sUSD on bZx (oracle attack)

This is where the oracle attack is executed.

I strongly recommend that you read this article from Sam Sun about oracle attacks, which explain how it works.

The idea is that bZx queries Kyber for the current ETH/sUSD rate, but now that the attacker distorted the market it will get an erroneous rate! This allows the attacker to borrow much more ETH than they could normally with this amount of sUSD, because bZx is fed a wrong price oracle.

To do it, they simply call the iETH contract’s borrowTokenFromDeposit() function. They send 1,099,841 sUSD (they bought the bought from the Synthetix Depot, and some more while distorting the Kyber prices), and are able to borrow 6796 ETH.

If you read the article about oracle attacks, you probably wonder how come the exploit worked. The article describes a very similar attack, and it was fixed.

Let’s quote the last fix that was implemented after that disclosure:

The bZx team reverted their changes for the previous attack and instead implemented a spread check, such that if the spread was above a certain threshold then the loan would be rejected. This solution handles the generic case so long as both tokens being queried has at least one non-manipulable reserve on Kyber, which is currently the case for all whitelisted tokens.

The check was implemented here:

require(
spreadPercentage <= maxSpread,
"bad price"
);

This means that bZx will check the price in both directions and look at the difference. However, here both reserves are Uniswap-like and both got their price manipulated.

So, yes, bZx was supposed to check for a small enough spread. But both these reserves do have a constant, small spread (that depends on their fees). So the checks did pass.

E. Settling the debt

Now that the attacker realized a profit in ETH, they can pay back the 7500 ETH, so the transaction can terminate correctly because the flash loan has been paid back.

Also note that if they wanted, the attacker could have pulled back the price that they skewed first for more profit. But if you look at the numbers you notice they spent 900 ETH to skew the price, compared to the 3518 ETH worth of sUSD that they bought from the Synthetix Depot and leveraged on bZx. Because the money they spent on skewing the price was only 10% of the amount they magically multiplied later, they didn’t need to care.

Preventative Techniques

By now, I hope that you’ve learned to recognize the common thread — it’s not always obvious that you’re using a price oracle and if you don’t follow the proper precautions, an attacker could trick your protocol into sending them all of your money. While there’s no one-size-fits-all fix that can be prescribed, here are a few solutions that have worked for other projects in the past. Maybe one of them will apply to you too.

Don’t use an on-chain decentralized oracle without some sort of validation

Due to the nature of on-chain decentralized oracles, ensure that you’re validating the rate being returned, whether it’s by taking the order (thereby nullifying any gains which may have been realized), comparing the rate against known good rates (in the case of DAI), or comparing the rate in both directions.

Let’s imagine that you want to find the price of WBTC in ETH. A seemingly obvious solution is to take the Uniswap V2 pair for ETH/WBTC, grab the reserve balance of ETH and WBTC, then divide the two. However, you’ve just calculated the spot price, and an attacker can easily manipulate it by buying or selling into the pool.

That seems fairly straightforward, but what if you actually want to calculate the price of a single ETH/WBTC LP token? It may be tempting to calculate the USD value of ETH and WBTC in order to calculate the total USD value of the pool. However, by doing this you’re actually incorporating the spot price because you’re still dependent on the reserve balances of the pool. This is an extremely subtle detail, and more than one project has been caught by it. You can read more about this footgun in this writeup by @cmichelio.

Consider the implications of dependencies on third-party projects

bZx assumed that Uniswap and Kyber would be a source of accurate price data. However, an accurate rate for a DEX means that a trade can be made using that rate, while an accurate rate for a DeFi project means that it is close to or equal to the FMV. In other words, an accurate rate for a DeFi project is an accurate rate for a DEX, but the opposite might not be true.

Furthermore, bZx’s second attempt at solving this problem was insufficient due to a misunderstanding in how the Kyber Network internally calculates the exchange rate between two non-ETH tokens.

As such, before introducing a dependency on a third-party project, consider not only whether the project has been audited, but also whether the project’s specifications and threat model align with your own. If you have the time, taking an in-depth look at their contracts also doesn’t hurt.

Shallow Markets, No Diving

Like diving into the shallow end of a pool, diving into a shallow market is painful and might result in significant expenses which will change your life forever. Before you even consider the intricacies of the specific price oracle you’re planning to use, consider whether the token is liquid enough to warrant integration with your platform.

A Bird in the Hand is Worth Two in the Bush

It may be mesmerizing to see the potential exchange rate on Uniswap, but nothing’s final until you actually click trade and the tokens are sitting in your wallet. Similarly, the best way to know for sure the exchange rate between two assets is to simply swap the assets directly. This approach is great because there’s no take-backs and no what-ifs. However, it may not work for protocols such as lending platforms which are required to hold on to the original asset.

Almost Decentralized Oracles

One way to summarize the problem with oracles that rely on on-chain data is that they’re a little too up-to-date. If that’s the case, why not introduce a bit of artificial delay? Write a contract which updates itself with the latest price from a decentralized exchange like Uniswap, but only when requested by a small group of privileged users. Now even if an attacker can manipulate the price, they can’t get your protocol to actually use it.

This approach is really simple to implement and is a quick win, but there are a few drawbacks — in times of chain congestion you might not be able to update the price as quickly as you’d like, and you’re still vulnerable to sandwich attacks. Also, now your users need to trust that you’ll actually keep the price updated.

Speed Bumps

Manipulating price oracles is a time-sensitive operation because arbitrageurs are always watching and would love the opportunity to optimize any suboptimal markets. If an attacker wants to minimize risk, they’ll want to do the two trades required to manipulate a price oracle in a single transaction so there’s no chance that an arbitrageur can jump in the middle. As a protocol developer, if your system supports it, it may be enough to simply implement a delay of as short as 1 block between a user entering and exiting your system.

This would remove the ability to perform deposits and withdrawals within a single transaction and therefore, make flash-loan based attacks infeasible. On users’ side, this would mean that during depositing, their tokens will be transferred into Harvest in one transaction. The users would subsequently claim their share in another transaction, ideally inside a different block. This would constitute a UX change and potentially incur a higher, yet still acceptable, gas cost for the depositors.

Of course, this might impact composability and miner collaboration with traders is on the rise. In the future, it may be possible for bad actors to perform price oracle manipulation across multiple transactions knowing that the miner they’ve partnered with will guarantee that no one can jump in the middle and take a bite out of their earnings.

Uniswap Time-Weighted Average Price (TWAP)

Uniswap V2 introduced a TWAP oracle for on-chain developers to use. The documentation goes into more detail on the exact security guarantees that the oracle provides, but in general for large pools over a long period of time with no chain congestion, the TWAP oracle is highly resistant to oracle manipulation attacks. However, due to the nature of its implementation, it may not respond quickly enough to moments of high market volatility and only works for assets for which there is already a liquid token on-chain.

The Uniswap TWAP oracle is available for any token which has a pair on Uniswap V2 or V3. It allows you to calculate the average price of the asset over some period of time up to a certain extent.

When to use

Use the Uniswap TWAP oracle if your token is listed on Uniswap V2 or V3 with sufficient trader activity and liquidity. In other words, it is expected that if there is an arbitrage opportunity, a trader should come and take it in order to rebalance the pool. If this is not the case, then it’s possible for an attacker to skew the price and simply wait for the TWAP to update.

Potential risks

You must decide on the time interval to use, which can be tricky. A shorter time interval means that you will see price updates quicker, but also lowers the cost of attack to manipulate the oracle. A longer time interval makes it much harder to manipulate the average price, but also means you won’t be able to react to volatility in the markets.

Example implementation

For Uniswap V2, see GitHub for an implementation of a 24 hour TWAP oracle.

For Uniswap V3, see GitHub for a library which you can integrate into your project.

{% hint style=“info” %} Related: https://shouldiusespotpriceasmyoracle.com/ {% endhint %}

Curve Virtual Price

Overview

Curve pools provide a function to calculate the price of a single LP token in a flash loan resistant manner.

When to use

If you need to calculate the price of Curve LP shares, then use the get_virtual_price function.

Potential risks

No additional risks besides dependency risk for every token the Curve pool supports.

Example implementation

See Curve’s documentation for more info.

Maker Price Feed

Overview

Maker operates their own network of price feeds which exposes data to whitelisted contracts on-chain. Projects can apply for access to the price data through the Maker governance process.

When to use

Use Maker’s price feeds if you think you can make it through the governance process and you would prefer to offload your oracle risks to the Maker oracle team.

Potential risks

You need to trust the Maker team and the anonymous feeds to behave correctly. However, the risk is low in practice given that Maker itself depends on these oracles. Also, as feed operators need to submit prices on chain manually, prices may be delayed during periods of extremely high chain congestion.

Example implementation

Submit a MIP10c9 subproposal to Maker Governance

M-of-N Reporters

Sometimes they say that if you want something done right, you do it yourself. What if you gather up N trusted friends and ask them to submit what they think is the right price on-chain, and the best M answers becomes the current price?

This approach is used by many large projects today: Maker runs a set of price feeds operated by trusted entities, Compound created the Open Oracle and features reporters such as Coinbase, and Chainlink aggregates price data from Chainlink operators and exposes it on-chain. Just keep in mind that if you choose to use one of these solutions, you’ve now delegated trust to a third party and your users will have to do the same. Requiring reporters to manually post updates on-chain also means that during times of high market volatility and chain congestion, price updates may not arrive on time.

Chainlink

Chainlink supports over 100 price feeds on Ethereum mainnet, mainly for pairs with ETH and pairs with USD. Developers can access this data for free simply by querying the smart contracts when needed.

When to use

Use Chainlink if you need pricing data for an asset that Maker or Uniswap doesn’t support, or if the latency associated with a TWAP oracle is unacceptable to your project.

Potential risks

Similar to Maker, you’ll need to trust the Chainlink team and node operators to behave correctly. Chainlink also requires node operators to push values on chain, so it may also suffer delays during periods of high chain congestion.

Example implementation

Refer to the Chainlink documentation for an example of how to get the price from a Chainlink aggregator smart contract.

A stricter configuration of the existing deposit arb check in the strategies

A stricter threshold ****that sets a maximum price fluctuation can make such an attack economically infeasible, however, it may be limiting deposits in the case of a natural impermanent loss effects.

Withdrawals in an underlying asset

When users deposit into vaults that use share pools (such as the Y pool), they effectively trade their single asset for the pool asset (such as the yCurve). If the users withdrew only the underlying asset, they would be able to trade them for an asset combination as per the current market conditions. If the market was manipulated, the trade would be also subject to such manipulation, which would prevent the attacking entity from generating a profit. From a regular user’s perspective, withdrawing yCRV could be followed by conversion into a stablecoin in a separate transaction. While this requires a UX change, this could also address the social slippage problem, and thus could be beneficial for the protocol. The disadvantage of this approach is that it binds the vault withdrawal mechanism to the strategy that is currently being used: if a strategy is switched to another strategy that does not use the shared underlying pool, or uses a different pool, the asset produced by the withdrawal would change too.

Using oracles for determining asset price

While an approximate asset price may be effectively determined from external oracles (provided by Chainlink or Maker), it would have a very loose connection to the real share price. If the value of assets inside the underlying DeFi protocol differed from the value reported by the oracle, the vault would be exposed to free arbitrage and a flashloan attack. This is not a solution for Harvest, however, oracles will be considered in the system design and possible mitigation strategies (as they have been considered up to this point).

Resources

--

--

Extropy.IO

Oxford-based blockchain and zero knowledge consultancy and auditing firm