---
# System prepended metadata

title: ABDK Uniswap LP Oracle Audit

---

# ABDK Uniswap LP Oracle Audit

**Customer**

MakerDAO

**Code**

The repository is located at https://github.com/makerdao/univ2-lp-oracle

The commit to be audited is c500d2197f153f70da0e3005e74b87c37ee33a40


**Tooling**

We utilize the DappHub dapptools suite for building and testing our contracts. The tools can be downloaded at [https://dapp.tools](https://dapp.tools) and you'll need `dapp` and `hevm`.

Setup:

```
$ git clone https://github.com/makerdao/univ2-lp-oracle.git
$ cd univ2-lp-oracle
$ dapp update
```

To build:

```
$ dapp --use solc:0.6.11 build
```

To test:

* Set `ETH_RPC_URL` envvar to an Ethereum RPC endpoint.
```
$ ./test.sh
```

**Model**

The model is described in the following Governance proposal on the Maker Forum

[https://forum.makerdao.com/t/uni-v2-dai-eth-collateral-onboarding-oracle-assessment-mip10c3-sp18/5499](https://forum.makerdao.com/t/uni-v2-dai-eth-collateral-onboarding-oracle-assessment-mip10c3-sp18/5499)

The following primitives are used based off the behavior of Uniswap Liquidity Pools:

(1) `Invariant k = reserve_x * reserve_y`

(2) `reserve_x * price_x = reserve_y * price_y`

The "raw" reserve balances in the Uniswap Liquidity Pool can be manipulated via a flash-loan or whale, hence we only use them to calculate the invariant `k`. However we can normalize the reserve balances to what they "should" be by using external Oracle prices for each asset using the following formulas derived from (1) and (2)

(3) `n_reserve_x = sqrt(k * price_y / price_x)`

(4) `n_reserve_y = sqrt*(k * price_x / price_y)`

The price of the LP token can be calculated by adding the value of the normalized reserve balances of each token together and dividing it over the entire LP token supply.

(5) `price_lp = ( (n_reserve_x * price_x) + (n_reserve_y * price_y) ) / supply_lp`

This can be simplified to:

(6) `price_lp = 2 * sqrt(k * p_x * p_y) / supply_lp`

**Assumptions**

This Oracle Model will have some price discrepancy from the actual price of an LP token due to the following two factors:

1. The model does not account for protocol fees should Uniswap Governance turn them on. This means that the longer no liquidity is added/removed to/from the Uniswap pool the more price diverges.
2. The model does not account for the .3% LP fee so arbitrageurs are not incentivized to perfectly balance the pool 50-50. This violates assumption (2) in our model.
3. Pool reserves can vary from block to block. This formula assumes that arbitrageurs will return the pool balances back to balance based on oracle prices. This avoids the reliance on reserve balances at a particular point in time, which may have been recently shifted due to a large trade.
4. Cost-effectiveness and simplicity outweighs marginal benefit of the precision.

These design decisions were chosen to simplify code complexity and reduce gas costs. The protocol is currently targeting high-liquidity, high-volume liquidity pairs, so it is expected that regular mints and burns will be transacted frequently throughout the course of trading.

**Price Delay**

The Maker Oracles use a delay of magnitude `hop` before the price is used by the Maker Protocol. This is to protect the system from liquidation risk as well as an Oracle attack. Users will have a `hop` period to recapitalize their positions or pay off debt before they are liquidated. The Maker Protocol benefits from users gracefully unwinding positions in the event of a market downturn rather than taking the risk of liquidations not returning an adequate amount of debt. The delay also ensures Maker Governance has a period `hop` to react to `stop` the Oracle in the event of an Oracle attack. These protections are vital to the health of the Maker Protocol and are valued much more than the downside of having a delayed price that does not correspond to the fair market value of the LP token.

**Dependencies**
Oracle inputs for the price of the LP token components are expected to come from a `Median` contract with an `OracleLike` interface.
Median Repository = https://github.com/makerdao/median/blob/master/src/median.sol

The validity of the price input from the Median contract for the price of each token is out of scope for this audit and can be assumed to be correct.


**Testing**

We've written tests which utilize `hevm` to pull and manipulate mainnet state of contracts we integrate with (such as Uniswap and Maker Medianizer Oracles). Some of these tests assert return values  are bounded by a reasonable range. These tests may fail if the price of ETH or BTC changes by a large magnitude such that the return values of some tests fall outside of that range. These breaking tests will just have their ranges updated over time as part of regular maintenance on the repository. Despite the chance that the tests may fail, we value pulling real mainnet state more than the alternative of pulling mocked state or pulling historical state which would require an archive node.

**Audit Goals and Deliverables**

* Analyze assumptions
* Validate math for correctness
* Under which conditions/scenarios will the model break?
* Bugs/exploits in the code
* Gas-optimizations
* Can the precision of the LP token price be bounded given the assumptions that are made? (i.e. < 2.5% ?)
* An audit report we are allowed to publish