diff --git a/contracts/UniswapV2.sol b/contracts/UniswapV2.sol index eddd138..ac64344 100644 --- a/contracts/UniswapV2.sol +++ b/contracts/UniswapV2.sol @@ -4,6 +4,7 @@ import "./interfaces/IUniswapV2.sol"; import "./libraries/Math.sol"; import "./libraries/SafeMath128.sol"; +import "./libraries/MOCK_Decimal.sol"; import "./token/ERC20.sol"; import "./token/SafeTransfer.sol"; @@ -22,8 +23,9 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran address public token1; TokenData private reserves; - TokenData private reservesCumulative; - TokenData private reservesCumulativeOverflows; + + MOCK_Decimal.Decimal private accumulatedPriceToken0; // mocked accumulated price of token0 / token1 + MOCK_Decimal.Decimal private accumulatedPriceToken1; // mocked accumulated price of token1 / token0 uint256 private blockNumberLast; bool private notEntered = true; @@ -77,39 +79,32 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran return (reserves.token0, reserves.token1); } - function getReservesCumulative() external view returns (uint128, uint128, uint128, uint128) { + function getAccumulatedPrices() external view returns (uint256, uint256) { require(blockNumberLast > 0, "UniswapV2: NOT_INITIALIZED"); - TokenData memory reservesCumulativeNext; - TokenData memory reservesCumulativeOverflowsNext; // replicate the logic in update if (block.number > blockNumberLast) { uint128 blocksElapsed = (block.number - blockNumberLast).downcast128(); - TokenData memory remaindersMul; - TokenData memory overflowsMul; - (remaindersMul.token0, overflowsMul.token0) = reserves.token0.omul(blocksElapsed); - (remaindersMul.token1, overflowsMul.token1) = reserves.token1.omul(blocksElapsed); + // get the prices according to the reserves as of the last official interaction with the contract + MOCK_Decimal.Decimal memory priceToken0 = MOCK_Decimal.div(reserves.token0, reserves.token1); + MOCK_Decimal.Decimal memory priceToken1 = MOCK_Decimal.div(reserves.token1, reserves.token0); - TokenData memory overflowsAdd; - (reservesCumulativeNext.token0, overflowsAdd.token0) = reservesCumulative.token0.oadd(remaindersMul.token0); - (reservesCumulativeNext.token1, overflowsAdd.token1) = reservesCumulative.token1.oadd(remaindersMul.token1); + // multiply these prices by the number of elapsed blocks + MOCK_Decimal.Decimal memory priceToken0Accumulated = MOCK_Decimal.mul(priceToken0, blocksElapsed); + MOCK_Decimal.Decimal memory priceToken1Accumulated = MOCK_Decimal.mul(priceToken1, blocksElapsed); - reservesCumulativeOverflowsNext = TokenData({ - token0: reservesCumulativeOverflows.token0.add(overflowsMul.token0.add(overflowsAdd.token0)), - token1: reservesCumulativeOverflows.token1.add(overflowsMul.token1.add(overflowsAdd.token1)) - }); + // add the accumulated prices to the global accumulators + return ( + MOCK_Decimal.add(accumulatedPriceToken0, priceToken0Accumulated).data, + MOCK_Decimal.add(accumulatedPriceToken1, priceToken1Accumulated).data + ); } else { - reservesCumulativeNext = reservesCumulative; - reservesCumulativeOverflowsNext = reservesCumulativeOverflows; + return ( + accumulatedPriceToken0.data, + accumulatedPriceToken1.data + ); } - - return ( - reservesCumulativeNext.token0, - reservesCumulativeNext.token1, - reservesCumulativeOverflowsNext.token0, - reservesCumulativeOverflowsNext.token1 - ); } function getBlockNumberLast() external view returns (uint256) { @@ -133,30 +128,17 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran if (blockNumberLast > 0) { uint128 blocksElapsed = (block.number - blockNumberLast).downcast128(); - // TODO address ratio of sum / sum of ratios / price accumulator issue + // get the prices according to the reserves as of the last official interaction with the contract + MOCK_Decimal.Decimal memory priceToken0 = MOCK_Decimal.div(reserves.token0, reserves.token1); + MOCK_Decimal.Decimal memory priceToken1 = MOCK_Decimal.div(reserves.token1, reserves.token0); - // multiply previous reserves by elapsed blocks in an overflow-safe way - TokenData memory remaindersMul; - TokenData memory overflowsMul; - (remaindersMul.token0, overflowsMul.token0) = reserves.token0.omul(blocksElapsed); - (remaindersMul.token1, overflowsMul.token1) = reserves.token1.omul(blocksElapsed); + // multiply these prices by the number of elapsed blocks + MOCK_Decimal.Decimal memory priceToken0Accumulated = MOCK_Decimal.mul(priceToken0, blocksElapsed); + MOCK_Decimal.Decimal memory priceToken1Accumulated = MOCK_Decimal.mul(priceToken1, blocksElapsed); - // update cumulative reserves in an overflow-safe way - TokenData memory overflowsAdd; - (reservesCumulative.token0, overflowsAdd.token0) = reservesCumulative.token0.oadd(remaindersMul.token0); - (reservesCumulative.token1, overflowsAdd.token1) = reservesCumulative.token1.oadd(remaindersMul.token1); - - // update cumulative reserves overflows - TokenData memory overflows = TokenData({ - token0: overflowsMul.token0.add(overflowsAdd.token0), - token1: overflowsMul.token1.add(overflowsAdd.token1) - }); - if (overflows.token0 > 0 || overflows.token1 > 0) { - reservesCumulativeOverflows = TokenData({ - token0: reservesCumulativeOverflows.token0.add(overflows.token0), - token1: reservesCumulativeOverflows.token1.add(overflows.token1) - }); - } + // add these incremental accumulated prices to the global accumulators + accumulatedPriceToken0 = MOCK_Decimal.add(accumulatedPriceToken0, priceToken0Accumulated); + accumulatedPriceToken1 = MOCK_Decimal.add(accumulatedPriceToken1, priceToken1Accumulated); } // update the last block number diff --git a/contracts/libraries/MOCK_Decimal.sol b/contracts/libraries/MOCK_Decimal.sol new file mode 100644 index 0000000..17c16c8 --- /dev/null +++ b/contracts/libraries/MOCK_Decimal.sol @@ -0,0 +1,20 @@ +pragma solidity 0.5.12; + +// mock fixed- or floating-point math +library MOCK_Decimal { + struct Decimal { + uint256 data; + } + + function div(uint128 numerator, uint128 denominator) internal pure returns (Decimal memory) { + return Decimal(numerator / denominator); + } + + function mul(Decimal memory a, uint128 b) internal pure returns (Decimal memory) { + return Decimal(a.data * b); + } + + function add(Decimal memory a, Decimal memory b) internal pure returns (Decimal memory) { + return Decimal(a.data + b.data); + } +} diff --git a/contracts/libraries/SafeMath128.sol b/contracts/libraries/SafeMath128.sol index 84c09ea..cdff376 100644 --- a/contracts/libraries/SafeMath128.sol +++ b/contracts/libraries/SafeMath128.sol @@ -10,15 +10,4 @@ library SafeMath128 { function mul(uint128 x, uint128 y) internal pure returns (uint128 z) { require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); } - - function oadd(uint128 w, uint128 x) internal pure returns (uint128 y, uint128 z) { - uint256 sum = uint256(w) + x; - z = uint128(sum / uint128(-1)); - y = uint128(sum % uint128(-1)); - } - function omul(uint128 w, uint128 x) internal pure returns (uint128 y, uint128 z) { - uint256 product = uint256(w) * x; - z = uint128(product / uint128(-1)); - y = uint128(product % uint128(-1)); - } }