Skip to content

Commit

Permalink
uniswap lib version updated
Browse files Browse the repository at this point in the history
uniswap libs added to oracles/external-modules
  • Loading branch information
aritkulova committed Sep 20, 2023
1 parent 6ec74e8 commit a191073
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .solcover.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
skipFiles: ["interfaces/", "mock/"],
skipFiles: ["interfaces/", "mock/", "oracles/external-modules/"],
configureYulOptimizer: true,
};
2 changes: 1 addition & 1 deletion contracts/mock/oracles/OracleMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.4;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {FixedPoint} from "@uniswap/lib/contracts/libraries/FixedPoint.sol";
import {FixedPoint} from "../../oracles/external-modules/uniswap-v2/lib/FixedPoint.sol";

import {Oracle} from "../../oracles/Oracle.sol";
import {UniswapV2PairMock} from "./UniswapV2PairMock.sol";
Expand Down
2 changes: 0 additions & 2 deletions contracts/mock/oracles/UniswapV2FactoryMock.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "hardhat/console.sol";

import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {UniswapV2PairMock} from "./UniswapV2PairMock.sol";

Expand Down
6 changes: 1 addition & 5 deletions contracts/oracles/Oracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini

import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {UniswapV2OracleLibrary} from "@uniswap/v2-periphery/contracts/libraries/UniswapV2OracleLibrary.sol";
import {UniswapV2OracleLibrary} from "./external-modules/uniswap-v2/v2-periphery/UniswapV2OracleLibrary.sol";

import {ArrayHelper} from "../libs/arrays/ArrayHelper.sol";

Expand Down Expand Up @@ -200,10 +200,6 @@ abstract contract Oracle is Initializable {
(blockTimestamp - blockTimestampOld);
}

if (price0 == 0) {
return 0;
}

return expectedToken_ == IUniswapV2Pair(pair_).token0() ? price0 : price1;
}
}
52 changes: 52 additions & 0 deletions contracts/oracles/external-modules/uniswap-v2/lib/Babylonian.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

// computes square roots using the babylonian method
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
library Babylonian {
// credit for this implementation goes to
// https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
function sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
// this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
// however that code costs significantly more gas
uint256 xx = x;
uint256 r = 1;
if (xx >= 0x100000000000000000000000000000000) {
xx >>= 128;
r <<= 64;
}
if (xx >= 0x10000000000000000) {
xx >>= 64;
r <<= 32;
}
if (xx >= 0x100000000) {
xx >>= 32;
r <<= 16;
}
if (xx >= 0x10000) {
xx >>= 16;
r <<= 8;
}
if (xx >= 0x100) {
xx >>= 8;
r <<= 4;
}
if (xx >= 0x10) {
xx >>= 4;
r <<= 2;
}
if (xx >= 0x8) {
r <<= 1;
}
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1; // Seven iterations should be enough
uint256 r1 = x / r;
return (r < r1 ? r : r1);
}
}
85 changes: 85 additions & 0 deletions contracts/oracles/external-modules/uniswap-v2/lib/BitMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

library BitMath {
// returns the 0 indexed position of the most significant bit of the input x
// s.t. x >= 2**msb and x < 2**(msb+1)
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0, "BitMath::mostSignificantBit: zero");

if (x >= 0x100000000000000000000000000000000) {
x >>= 128;
r += 128;
}
if (x >= 0x10000000000000000) {
x >>= 64;
r += 64;
}
if (x >= 0x100000000) {
x >>= 32;
r += 32;
}
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 0x4) {
x >>= 2;
r += 2;
}
if (x >= 0x2) r += 1;
}

// returns the 0 indexed position of the least significant bit of the input x
// s.t. (x & 2**lsb) != 0 and (x & (2**(lsb) - 1)) == 0)
// i.e. the bit at the index is set and the mask of all lower bits is 0
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0, "BitMath::leastSignificantBit: zero");

r = 255;
if (x & type(uint128).max > 0) {
r -= 128;
} else {
x >>= 128;
}
if (x & type(uint64).max > 0) {
r -= 64;
} else {
x >>= 64;
}
if (x & type(uint32).max > 0) {
r -= 32;
} else {
x >>= 32;
}
if (x & type(uint16).max > 0) {
r -= 16;
} else {
x >>= 16;
}
if (x & type(uint8).max > 0) {
r -= 8;
} else {
x >>= 8;
}
if (x & 0xf > 0) {
r -= 4;
} else {
x >>= 4;
}
if (x & 0x3 > 0) {
r -= 2;
} else {
x >>= 2;
}
if (x & 0x1 > 0) r -= 1;
}
}
164 changes: 164 additions & 0 deletions contracts/oracles/external-modules/uniswap-v2/lib/FixedPoint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import {FullMath} from "./FullMath.sol";
import {Babylonian} from "./Babylonian.sol";
import {BitMath} from "./BitMath.sol";

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
library FixedPoint {
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
struct uq112x112 {
uint224 _x;
}

// range: [0, 2**144 - 1]
// resolution: 1 / 2**112
struct uq144x112 {
uint256 _x;
}

uint8 public constant RESOLUTION = 112;
uint256 public constant Q112 = 0x10000000000000000000000000000; // 2**112
uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; // 2**224
uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits)

// encode a uint112 as a UQ112x112
function encode(uint112 x) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(x) << RESOLUTION);
}

// encodes a uint144 as a UQ144x112
function encode144(uint144 x) internal pure returns (uq144x112 memory) {
return uq144x112(uint256(x) << RESOLUTION);
}

// decode a UQ112x112 into a uint112 by truncating after the radix point
function decode(uq112x112 memory self) internal pure returns (uint112) {
return uint112(self._x >> RESOLUTION);
}

// decode a UQ144x112 into a uint144 by truncating after the radix point
function decode144(uq144x112 memory self) internal pure returns (uint144) {
return uint144(self._x >> RESOLUTION);
}

// multiply a UQ112x112 by a uint, returning a UQ144x112
// reverts on overflow
function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) {
uint256 z = 0;
require(y == 0 || (z = self._x * y) / y == self._x, "FixedPoint::mul: overflow");
return uq144x112(z);
}

// multiply a UQ112x112 by an int and decode, returning an int
// reverts on overflow
function muli(uq112x112 memory self, int256 y) internal pure returns (int256) {
uint256 z = FullMath.mulDiv(self._x, uint256(y < 0 ? -y : y), Q112);
require(z < 2 ** 255, "FixedPoint::muli: overflow");
return y < 0 ? -int256(z) : int256(z);
}

// multiply a UQ112x112 by a UQ112x112, returning a UQ112x112
// lossy
function muluq(
uq112x112 memory self,
uq112x112 memory other
) internal pure returns (uq112x112 memory) {
if (self._x == 0 || other._x == 0) {
return uq112x112(0);
}
uint112 upper_self = uint112(self._x >> RESOLUTION); // * 2^0
uint112 lower_self = uint112(self._x & LOWER_MASK); // * 2^-112
uint112 upper_other = uint112(other._x >> RESOLUTION); // * 2^0
uint112 lower_other = uint112(other._x & LOWER_MASK); // * 2^-112

// partial products
uint224 upper = uint224(upper_self) * upper_other; // * 2^0
uint224 lower = uint224(lower_self) * lower_other; // * 2^-224
uint224 uppers_lowero = uint224(upper_self) * lower_other; // * 2^-112
uint224 uppero_lowers = uint224(upper_other) * lower_self; // * 2^-112

// so the bit shift does not overflow
require(upper <= type(uint112).max, "FixedPoint::muluq: upper overflow");

// this cannot exceed 256 bits, all values are 224 bits
uint256 sum = uint256(upper << RESOLUTION) +
uppers_lowero +
uppero_lowers +
(lower >> RESOLUTION);

// so the cast does not overflow
require(sum <= type(uint224).max, "FixedPoint::muluq: sum overflow");

return uq112x112(uint224(sum));
}

// divide a UQ112x112 by a UQ112x112, returning a UQ112x112
function divuq(
uq112x112 memory self,
uq112x112 memory other
) internal pure returns (uq112x112 memory) {
require(other._x > 0, "FixedPoint::divuq: division by zero");
if (self._x == other._x) {
return uq112x112(uint224(Q112));
}
if (self._x <= type(uint144).max) {
uint256 value = (uint256(self._x) << RESOLUTION) / other._x;
require(value <= type(uint224).max, "FixedPoint::divuq: overflow");
return uq112x112(uint224(value));
}

uint256 result = FullMath.mulDiv(Q112, self._x, other._x);
require(result <= type(uint224).max, "FixedPoint::divuq: overflow");
return uq112x112(uint224(result));
}

// returns a UQ112x112 which represents the ratio of the numerator to the denominator
// can be lossy
function fraction(
uint256 numerator,
uint256 denominator
) internal pure returns (uq112x112 memory) {
require(denominator > 0, "FixedPoint::fraction: division by zero");
if (numerator == 0) return FixedPoint.uq112x112(0);

if (numerator <= type(uint144).max) {
uint256 result = (numerator << RESOLUTION) / denominator;
require(result <= type(uint224).max, "FixedPoint::fraction: overflow");
return uq112x112(uint224(result));
} else {
uint256 result = FullMath.mulDiv(numerator, Q112, denominator);
require(result <= type(uint224).max, "FixedPoint::fraction: overflow");
return uq112x112(uint224(result));
}
}

// take the reciprocal of a UQ112x112
// reverts on overflow
// lossy
function reciprocal(uq112x112 memory self) internal pure returns (uq112x112 memory) {
require(self._x != 0, "FixedPoint::reciprocal: reciprocal of zero");
require(self._x != 1, "FixedPoint::reciprocal: overflow");
return uq112x112(uint224(Q224 / self._x));
}

// square root of a UQ112x112
// lossy between 0/1 and 40 bits
function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) {
if (self._x <= type(uint144).max) {
return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112)));
}

uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x);
safeShiftBits -= safeShiftBits % 2;
return
uq112x112(
uint224(
Babylonian.sqrt(uint256(self._x) << safeShiftBits) <<
((112 - safeShiftBits) / 2)
)
);
}
}
43 changes: 43 additions & 0 deletions contracts/oracles/external-modules/uniswap-v2/lib/FullMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: CC-BY-4.0
pragma solidity ^0.8.4;

// taken from https://medium.com/coinmonks/math-in-solidity-part-3-percents-and-proportions-4db014e080b1
// license is CC-BY-4.0
library FullMath {
function fullMul(uint256 x, uint256 y) internal pure returns (uint256 l, uint256 h) {
uint256 mm = mulmod(x, y, type(uint256).max);
l = x * y;
h = mm - l;
if (mm < l) h -= 1;
}

function fullDiv(uint256 l, uint256 h, uint256 d) private pure returns (uint256) {
uint256 pow2 = d & (~d + 1);
d /= pow2;
l /= pow2;
l += h * ((~pow2 + 1) / pow2 + 1);
uint256 r = 1;
r *= 2 - d * r;
r *= 2 - d * r;
r *= 2 - d * r;
r *= 2 - d * r;
r *= 2 - d * r;
r *= 2 - d * r;
r *= 2 - d * r;
r *= 2 - d * r;
return l * r;
}

function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
(uint256 l, uint256 h) = fullMul(x, y);

uint256 mm = mulmod(x, y, d);
if (mm > l) h -= 1;
l -= mm;

if (h == 0) return l / d;

require(h < d, "FullMath: FULLDIV_OVERFLOW");
return fullDiv(l, h, d);
}
}
Loading

0 comments on commit a191073

Please sign in to comment.