Proposal To Upgrade To UAV V3

Hi everyone, over the last several months we have been working with the folks at Chainlink to develop the UniswapAnchorView (UAV) V3. OZ has successfully audited the contract and it is ready for implementation.

Overview

Below are the high-level changes and improvements:

  • UAV V3 uses Uniswap V3 Oracles instead of V2
  • Increased number of markets available

The shift from using UNI-V2 to UNI-V3 is significant because V2 liquidity is drying up as liquidity providers move to V3 to capitalize on concentrated liquidity provisioning strategies. Having Compound’s UAV Oracle mimic this shift provides higher reliability and more security by reducing the risk of price manipulation or any other issues related to low liquidity.

UAV V3

The code can be found and reviewed on the UAV V3 Github page.

Below is the summary of changes:

  • The new UniswapAnchoredView (UAV) no longer records and keeps observations but instead queries the respective Uniswap V3 pool’s observe function to get a TWAP with the set anchor period when it’s needed (i.e., when a reporter calls validate on the UAV).
  • The price of an asset in Uniswap V3 is a function of the “tick” of the pool. The formula is price = 1.0001^tick. The math required for conversion between posted prices and the Uniswap V3 TWAP and the UAV representation has been modified to support this. Relevant libraries (TickMath and FullMath) have been included from the Uniswap V3 codebase.
  • The tests from the original UAV have been adapted to the new Uniswap V3 architecture - i.e., observations/TWAP tests were removed. Hardhat (+typechain) has been implemented so real Uniswap V3 pools can be used via forked mainnet in tests and eliminate the need for mocking Uniswap V3 pools.

This code has been audited, which you can see the results here: compound-uav3-audit - Google Drive

Below is a list of coins the protocol supports in the existing oracle contract and the TVL of the anchor market. Overall the change from v2 to v3 is very positive, however, there are a few markets where it is less.

Liquidity v2

ETH: $165m - 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc
DAI: $18m - 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11
USDC: N/A
USDT: N/A
WBTC: $21m - 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940
BAT: $370k - 0xB6909B960DbbE7392D405429eB2b3649752b4838
ZRX: $268k - 0xc6F348dd3B91a56D117ec0071C1e9b83C0996De4
REPv2: $347k - 0x8979A3Ef9D540480342AC0F56e9D4c88807b1CBa
SAI: N/A
UNI: $22m - 0xd3d2E2692501A5c9Ca623199D38826e513033a17
COMP: $246k - 0xCFfDdeD873554F362Ac02f8Fb1f02E5ada10516f
LINK: $3m - 0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974
TUSD: N/A
AAVE: $496k - 0xDFC14d2Af169B0D36C4EFF567Ada9b2E0CAE044f
SUSHI: $490k - 0xCE84867c3c02B05dc570d0135103d3fB9CC19433
MKR: $4m - 0x95b4eF2869eBD94BEb4eEE400a99824BF5DC325b
YFI: $661k - 0x2fDbAdf3C4D5A8666Bc06645B8358ab803996E28
USDP: N/A
FEI: $1m - 0x7713DD9Ca933848F6819F38B8352D9A15EA73F67

Not yet added to the protocol, but on the oracle contract:
MATIC: $1m - 0x819f3450dA6f110BA6Ea52195B3beaFa246062dE
RAI: $4m - 0x8aE720a71622e824F576b4A8C03031066548A3B1
LUSD $28k - 0xF20EF17b889b437C151eB5bA15A47bFc62bfF469
FRAX: $137k - 0xFD0A40Bc83C5faE4203DEc7e5929B446b07d1C76

Liquidity v3

ETH: $308m - 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640
DAI: $54m - 0xc2e9f25be6257c210d7adf0d4cd6e3e881ba25f8
USDC: N/A
USDT: N/A
WBTC: $267m - 0xcbcdf9626bc03e24f779434178a73a0b4bad62ed
BAT: $1m - 0xae614a7a56cb79c04df2aeba6f5dab80a39ca78e
ZRX: $880k - 0x14424eeecbff345b38187d0b8b749e56faa68539
REPv2: $19k - 0xb055103b7633b61518cd806d95beeb2d4cd217e7
SAI: N/A
UNI: $12m - 0x1d42064fc4beb5f8aaf85f4617ae8b3b5b8bd801
COMP: $6m - 0xea4ba4ce14fdd287f380b55419b1c5b6c3f22ab6
LINK: $13m - 0xa6cc3c2531fdaa6ae1a3ca84c2855806728693e8
AAVE: $4m - 0x5ab53ee1d50eef2c1dd3d5402789cd27bb52c1bb
SUSHI: $280k - 0x73a6a761fe483ba19debb8f56ac5bbf14c0cdad1
MKR: $13m - 0xe8c6c9227491c0a8156a0106a0204d881bb7e531
YFI: $2m - 0x04916039b1f59d9745bf6e0a21f191d1e0a84287

Not yet added to the protocol, but on the oracle contract:
MATIC: $11m - 0x290a6a7460b308ee3f19023d2d00de604bcf5b42
RAI: $106k - 0x14de8287adc90f0f95bf567c0707670de52e3813
LUSD $114k- 0x9663f2ca0454accad3e094448ea6f77443880454
FRAX: couldn’t find anything worth referencing.

7 Likes

Great job @getty on getting this done! Having liquidity to back up our back-stop prices is important to have reliable, accurate, and manipulation minimized prices. Liquidity can move around making it important to have our code follow.

I’d like to share some words of caution when using Uniswap v3 as an anchor. While concentrated liquidity is better for traders, it’s worse for providing accurate and reliable oracle data.

A lack of liquidity higher or lower can mean it’s really easy to manipulate the price. Let’s take the COMP-ETH pool with the highest TVL (the 0.3% fee pool):

We see a lot of sell positions at the 0.0413 ETH price, a lot less between 0.0413 and the current price of 0.0354, and barely any buy positions below that price.

There’s very little liquidity supporting the price, so if someone were to dump COMP on this specific pool, the price would take a massive hit and could stay there for a while. COMP prices would then fail to update, or bad prices could slip through, or in the case the failover was active, we could suffer gruesome attacks.

We also noticed this with the UST-USDC pairs with the recent de-pegging event where UST’s Uniswap v3 price was at about $0.91 for a number of days while it was actually trading at a small fraction of that. This problem could have possibly affected other pairs as well.

From an oracle developer standpoint, I recommend having the requirement of sufficient full-range liquidity with some sort of guaranteed lock-up to prevent the liquidity from being removed.

6 Likes

Very cool! It seems to me like the scenario @TylerEther offers could be at least partially mitigated through a wrapper contract that simply takes a TVL-weighted average of the UAV V2 and UAV V3. Perhaps the benefits aren’t sufficient to justify the extra gas, but curious about your thoughts on whether UAV V2 could play a role in mitigating the concentrated liquidity risks of V3. Do you or others have a write-up relevant to these ideas that we could check out? Thanks!

1 Like

It’s best to not use stale prices, but doing so would damper the impact of such a failure scenario.

Aggregating Uniswap V2 and V3 directly on the other hand would result in a really strong price. I have roughly 4 months of data to support this - let me know if you want access.

1 Like

Fewer changes, fewer mistakes, less protocol revenue, less attention.

I’m curious what Compound’s past with using the failover has been. Do chainlink oracles go down for extended periods of time? Have any TWAP attacks been tried before?

This would mean aggregating and averaging V2 and V3 prices weighted by their respective liquidity, correct? Many newer tokens have a majority of liquidity concentrated on V3 - would hate to see low liquidity on V2 be an attack vector or a barrier to asset listing.

Thanks for working on this @GFXlabs - low liquidity on V2 has been a barrier to asset listing for many great projects.

I’m curious what Compound’s past with using the failover has been. Do chainlink oracles go down for extended periods of time? Have any TWAP attacks been tried before?

The failover has never been activated. So far we haven’t experienced any issues although the price feeds aren’t being monitored as far as I’m aware.

This would mean aggregating and averaging V2 and V3 prices weighted by their respective liquidity, correct? Many newer tokens have a majority of liquidity concentrated on V3 - would hate to see low liquidity on V2 be an attack vector or a barrier to asset listing.

Aggregating prices across multiple DEXs would be best. There’s not always going to be sufficient liquidity on Uniswap V2 or even V3. Sometimes the majority of liquidity can even be on Sushiswap or Curve in rare cases such as stETH-ETH. Fragmented liquidity has been a barrier to asset listing in the past and will likely continue to be a barrier, unfortunately.

I’d like to note that the minimum liquidity required for a Uniswap V2 pool is a lot lower than most people think. If we want the price to be 99% accurate, it must be profitable to perform arbitrage on the pool resulting in about a 1% change in price. This is a function of both liquidity and gas price. Because we use TWAPs (assuming the pool has minimum required liquidity for an accurate price), an attacker would have to manipulate the price every block over the TWAP period. It becomes even more expensive with miners performing arbitrage as well.

The real question becomes how do we guarantee that the fallback pool always has enough liquidity for an accurate price.

1 Like

I also have a comment on Uniswap v3’s observation cardinalities.

All of the UAV price updates call the Uniswap v3 pool oracle’s observe function. This function will revert if the pool oracle doesn’t have an observation for the past 15 minutes (i.e. the TWAP period).

Up to one observation is written per block (if there’s at least one trade in the block). So we must handle the worst case scenario where there’s a trade in the pool every block. Therefore, the underlying pool oracle’s observation cardinality must be at least 15 minutes / blockTime = 900 seconds / 10 seconds = 90. We use 10 seconds for block time as that’s what’s expected after the merge. Since blocks aren’t produced exactly every 10 seconds (or 11 seconds on average currently), we must use a bit higher than a cardinality of 90. I’d suggest requiring an observation cardinality of 120 to be safe.