forked from roynalnaruto/moebius
-
Notifications
You must be signed in to change notification settings - Fork 0
/
UniswapOracle.sol
92 lines (76 loc) · 3.58 KB
/
UniswapOracle.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
pragma solidity ^0.7.0;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/v2-periphery/contracts/interfaces/IERC20.sol';
import '@uniswap/lib/contracts/libraries/FixedPoint.sol';
import './libraries/UniswapV2OracleLibrary.sol';
import './libraries/UniswapV2Library.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 UniswapOracle {
using FixedPoint for *;
uint public constant PERIOD = 1 minutes;
bytes32 public programId;
bytes32 public accountId;
IUniswapV2Pair immutable pair;
address public immutable token0;
address public immutable token1;
uint public price0CumulativeLast;
uint public price1CumulativeLast;
uint32 public blockTimestampLast;
FixedPoint.uq112x112 public price0Average;
FixedPoint.uq112x112 public price1Average;
constructor(
bytes32 _programId,
bytes32 _accountId,
address _factory,
address _tokenA,
address _tokenB
) {
IUniswapV2Pair _pair = IUniswapV2Pair(UniswapV2Library.pairFor(_factory, _tokenA, _tokenB));
pair = _pair;
token0 = _pair.token0();
token1 = _pair.token1();
price0CumulativeLast = _pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
price1CumulativeLast = _pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
uint112 reserve0;
uint112 reserve1;
(reserve0, reserve1, blockTimestampLast) = _pair.getReserves();
require(reserve0 != 0 && reserve1 != 0, 'ExampleOracleSimple: NO_RESERVES'); // ensure that therbe's liquidity in the pair
programId = _programId;
accountId = _accountId;
}
function updateAndConsult(address token, uint amountIn)
external
returns (bytes32 _programId, bytes32 _accountId, bytes memory _packedData)
{
(uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =
UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
// ensure that at least one full period has passed since the last update
require(timeElapsed >= PERIOD, 'ExampleOracleSimple: PERIOD_NOT_ELAPSED');
// 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;
blockTimestampLast = blockTimestamp;
uint amountOut = consult(token, amountIn);
uint _amount0 = token == token0 ? amountIn : amountOut;
uint _amount1 = token == token0 ? amountOut : amountIn;
_programId = programId;
_accountId = accountId;
_packedData = abi.encode(token0, _amount0, token1, _amount1);
}
// note this will always return 0 before update has been called successfully for the first time.
function consult(address token, uint amountIn) public view returns (uint amountOut) {
if (token == token0) {
amountOut = price0Average.mul(amountIn).decode144();
} else {
require(token == token1, 'ExampleOracleSimple: INVALID_TOKEN');
amountOut = price1Average.mul(amountIn).decode144();
}
}
}