Conversation
038266c to
b2a2ff6
Compare
2da7e02 to
e0d7604
Compare
b4b4a6d to
1fc085b
Compare
6a36429 to
41fa50e
Compare
a74e05a to
d8cb3e6
Compare
d8cb3e6 to
269be08
Compare
| validatePoolLiquidity(pool, oracleConfig.minPoolLiquidity); | ||
|
|
||
| uint8 inputTokenDecimals = ERC20(inputToken).decimals(); | ||
| uint8 outputTokenDecimals = ERC20(outputToken).decimals(); |
There was a problem hiding this comment.
Gas optimization: Set both of these decimal values as immutable global variables in the constructor so that they don't have to do these redundant 2 calls + SLOADs on the read.
| override | ||
| hasAnyOfThreeRoles( | ||
| TribeRoles.GOVERNOR, | ||
| TribeRoles.GUARDIAN, |
There was a problem hiding this comment.
Is this a power we want to give to a guardian?
| uint256 denominator; | ||
| if (!invertPrice) { | ||
| // price = ((sqrtPrice*2^96) * (sqrtPrice*2^96) * decimalNormaliser) / (2^96)^2 | ||
| numerator = !invertDecNormaliser |
There was a problem hiding this comment.
If statement would be more legible here.
| //price = ((2^96)^2 * decimalNormaliser) / (sqrtPrice*2^96) * (sqrtPrice*2^96) | ||
| numerator = !invertDecNormaliser | ||
| ? FullMath.mulDiv(2**192, decimalNormaliser, uint256(1)) | ||
| : FullMath.mulDiv(2**192, uint256(1), decimalNormaliser); |
There was a problem hiding this comment.
If statement would be more legible here.
| assertTrue(valid); | ||
|
|
||
| assertGt(price.value, 2000e18); | ||
| assertLt(price.value, 5000e18); |
There was a problem hiding this comment.
This test is pretty fragile, I would recommend either widening the range to 1k-10k, or just testing that spot price isn't more than 10 percent away from the TWAP price.
|
|
||
| assertTrue(valid); | ||
| assertGt(price.value, 2e18 + 5e17); | ||
| assertLt(price.value, 3e18 + 5e17); |
There was a problem hiding this comment.
Same comment about test being fragile.
| assertTrue(valid); | ||
|
|
||
| assertGt(price.value, 40000e18); | ||
| assertLt(price.value, 50000e18); |
There was a problem hiding this comment.
This test is fragile and needs to be reworked.
Summary
Adds UniswapV3 TWAP backup oracles to the
PegStabilityModules(PSMs) forDAIandETH. These both have highly liquidUSDCpools on Uniswap V3:DAI-USDC: ~$300m TVLUSDC-ETH: ~$285m TVLDescription
A
UniswapV3OracleWrapperhas been created which implementsIOracle.sol. Each asset requires its ownUniswapV3OracleWrappercontract to be deployed.Calculating oracle price and siloing out Uniswap logic
The Maths of calculating the oracle price is siloed out into
UniswapWrapper.sol. This is mostly done because the UniswapV3 contracts are written in a version,>=0.5.0 <0.8.0, which is incompatible with the Fei codebase. They rely on underflows and overflows happening in some of their accounting logic, which in >0.8.0 causes reverts.Oracle config and safety
The oracles settings are controlled by an
OracleConfigthat is injected in. It has the following form:The
minPoolLiquidityconfig is a safety parameter that is used to enforce a minimum level of liquidity in the relevant pool in order to provide a valid reading.Testing
Integration tests which fork
mainnethave been added to thecontracts/test/integrationfolder. Five oracles have been deployed against the corresponding pools:daiUsdcPoolusdcEthPoolwbtcUsdcPoolusdtAvinocPoolusdcFeiPoolThe prices returned by the relevant TWAP oracles have been validated manually against the relevant pool spot price. These 5 in particular were chosen because they have parameters (token0, token1, inputNumDecimals, outputNumDecimals) which allow all 4 paths of the Uniswap maths calculation logic to be tested. Details are outlined in the integration tests.
Concern
I think there is a broken abstraction in
OracleRef.solwhere the decimal normalisation and inversion occurs globally and applies to both the primary and backup oracle price results.As in this PR, these considerations should be pushed down into the relevant oracle contract. These factors likely need to be taken into account in different ways for different oracles depending on how they calculate the price. For example in calculating the Uni TWAP price, we need to know whether we are inverting when we are calculating the
numeratoranddenominator.If this UniV3 backupOracle was to be set, and
setDecimalsNormalizer()orsetDoInvert()were later seperately called onOracleRef.sol- it would result in the backup oracleOracleRef.solreporting the incorrect price due to this global post processing.Before deploying this PR, I believe we should:
setDecimalsNormalizer()orsetDoInvert()fromOracleRef.soland push these considerations down into the primary Chainlink oracle contract