Skip to content

Commit

Permalink
Use @uniswap/lib and simplify example oracle (Uniswap#15)
Browse files Browse the repository at this point in the history
* Use @uniswap/lib and simplify example oracle

* Assert => require
Improve some documentation

* Remove pair.sync in favor of counterfactual

* Add a disclaimer
  • Loading branch information
moodysalem committed Apr 23, 2020
1 parent 97a1f95 commit c5ce895
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 80 deletions.
38 changes: 21 additions & 17 deletions contracts/ExampleOracleSimple.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ pragma solidity =0.6.6;

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/lib/contracts/libraries/FixedPoint.sol';

import './libraries/UQ112x112.sol';

// fixed window oracle that recomputes the average price for the entire period once every period
// note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period
contract ExampleOracleSimple {
using UQ112x112 for uint224;
using FixedPoint for *;

uint public constant PERIOD = 24 hours;

Expand All @@ -17,8 +18,8 @@ contract ExampleOracleSimple {
uint public price0CumulativeLast;
uint public price1CumulativeLast;
uint32 public blockTimestampLast;
uint224 public price0Average;
uint224 public price1Average;
FixedPoint.uq112x112 public price0Average;
FixedPoint.uq112x112 public price1Average;

constructor(address factory, address tokenA, address tokenB) public {
IUniswapV2Pair _pair = IUniswapV2Pair(IUniswapV2Factory(factory).getPair(tokenA, tokenB));
Expand All @@ -30,30 +31,33 @@ contract ExampleOracleSimple {
uint112 reserve0;
uint112 reserve1;
(reserve0, reserve1, blockTimestampLast) = _pair.getReserves();
assert(reserve0 != 0 && reserve1 != 0); // ensure that there's liquidity in the pair
assert(blockTimestampLast != 0); // ensure there's a price history
require(reserve0 != 0 && reserve1 != 0, 'ExampleOracleSimple: NO_RESERVES'); // ensure that there's liquidity in the pair
require(blockTimestampLast != 0, 'ExampleOracleSimple: NO_PRICE_HISTORY'); // ensure there's a price history
}

function update() external {
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
assert(timeElapsed >= PERIOD); // ensure that at least one full period has passed since the last update

// ensure that at least one full period has passed since the last update
require(timeElapsed >= PERIOD, 'ExampleOracleSimple: PERIOD_NOT_ELAPSED');

uint price0Cumulative = pair.price0CumulativeLast();
uint price1Cumulative = pair.price1CumulativeLast();

// if time has elapsed since the last update on the pair, mock the accumulated price values
// alternately, call pair.sync() before reading the cumulative last price from the pair
(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLastFromPair) = pair.getReserves();
assert(reserve0 != 0 && reserve1 != 0); // ensure that there's still liquidity in the pair
if (blockTimestampLastFromPair != blockTimestamp) {
uint timeElapsedPartial = blockTimestamp - blockTimestampLastFromPair; // overflow is desired
price0Cumulative += uint(UQ112x112.encode(reserve1).uqdiv(reserve0)) * timeElapsedPartial; // counterfactual
price1Cumulative += uint(UQ112x112.encode(reserve0).uqdiv(reserve1)) * timeElapsedPartial; // counterfactual
price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsedPartial; // counterfactual
price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsedPartial; // counterfactual
}

// - overflow is desired, / never overflows, casting never truncates
price0Average = uint224((price0Cumulative - price0CumulativeLast) / timeElapsed);
price1Average = uint224((price1Cumulative - price1CumulativeLast) / timeElapsed);
// overflow is desired, casting never truncates
// cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));

price0CumulativeLast = price0Cumulative;
price1CumulativeLast = price1Cumulative;
Expand All @@ -62,10 +66,10 @@ contract ExampleOracleSimple {

function consult(address token, uint amountIn) external view returns (uint amountOut) {
if (token == token0) {
amountOut = UQ112x112.decode(price0Average.uqmul(amountIn));
amountOut = price0Average.mul(amountIn).decode144();
} else {
assert(token == token1);
amountOut = UQ112x112.decode(price1Average.uqmul(amountIn));
require(token == token1, 'ExampleOracleSimple: INVALID_TOKEN');
amountOut = price1Average.mul(amountIn).decode144();
}
}
}
2 changes: 1 addition & 1 deletion contracts/ExampleSwapToPrice.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
pragma solidity =0.6.6;

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import './UniswapV2Library.sol';
import './interfaces/IERC20.sol';
import './interfaces/IUniswapV2Router01.sol';
import './libraries/SafeMath.sol';
import './libraries/TransferHelper.sol';

contract ExampleSwapToPrice is UniswapV2Library {
using SafeMath for uint256;
Expand Down
9 changes: 5 additions & 4 deletions contracts/UniswapV2Migrator.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
pragma solidity =0.6.6;

import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import './interfaces/IUniswapV2Migrator.sol';
import './interfaces/V1/IUniswapV1Factory.sol';
import './interfaces/V1/IUniswapV1Exchange.sol';
import './interfaces/IUniswapV2Router01.sol';
import './interfaces/IERC20.sol';
import './libraries/TransferHelper.sol';

contract UniswapV2Migrator is IUniswapV2Migrator {
IUniswapV1Factory immutable factoryV1;
IUniswapV2Router01 immutable router;

constructor(address _factoryV1, address _router) public {
factoryV1 = IUniswapV1Factory(_factoryV1);
router = IUniswapV2Router01(_router);
constructor(address _factoryV1, address _router) public {
factoryV1 = IUniswapV1Factory(_factoryV1);
router = IUniswapV2Router01(_router);
}

// needs to accept ETH from any v1 exchange and the router. ideally this could be enforced, as in the router,
Expand Down
2 changes: 1 addition & 1 deletion contracts/UniswapV2Router01.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
pragma solidity =0.6.6;

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import './interfaces/IUniswapV2Router01.sol';
import './UniswapV2Library.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
import './libraries/TransferHelper.sol';

contract UniswapV2Router01 is IUniswapV2Router01, UniswapV2Library {
address public immutable override WETH;
Expand Down
27 changes: 0 additions & 27 deletions contracts/libraries/TransferHelper.sol

This file was deleted.

30 changes: 0 additions & 30 deletions contracts/libraries/UQ112x112.sol

This file was deleted.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"contracts"
],
"dependencies": {
"@uniswap/lib": "1.1.1",
"@uniswap/v2-core": "1.0.0"
},
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.35.tgz#1e61b226c14380f4384f70cfe49a65c2c553ad2b"
integrity sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==

"@uniswap/lib@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-1.1.1.tgz#0afd29601846c16e5d082866cbb24a9e0758e6bc"
integrity sha512-2yK7sLpKIT91TiS5sewHtOa7YuM8IuBXVl4GZv2jZFys4D2sY7K5vZh6MqD25TPA95Od+0YzCVq6cTF2IKrOmg==

"@uniswap/v2-core@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.0.tgz#e0fab91a7d53e8cafb5326ae4ca18351116b0844"
Expand Down

0 comments on commit c5ce895

Please sign in to comment.