From fe96d9a789d3a4979e2e21c9dc2647de856f5f03 Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Thu, 21 Nov 2019 18:01:49 -0500 Subject: [PATCH] add skeleton Q104.104 implementation --- contracts/UniswapV2.sol | 46 ++++++++++++---------------- contracts/libraries/MOCK_Decimal.sol | 20 ------------ contracts/libraries/SafeMath256.sol | 5 +++ contracts/libraries/UQ104x104.sol | 17 ++++++++++ 4 files changed, 42 insertions(+), 46 deletions(-) delete mode 100644 contracts/libraries/MOCK_Decimal.sol create mode 100644 contracts/libraries/UQ104x104.sol diff --git a/contracts/UniswapV2.sol b/contracts/UniswapV2.sol index ac64344..9e80ad0 100644 --- a/contracts/UniswapV2.sol +++ b/contracts/UniswapV2.sol @@ -4,7 +4,7 @@ import "./interfaces/IUniswapV2.sol"; import "./libraries/Math.sol"; import "./libraries/SafeMath128.sol"; -import "./libraries/MOCK_Decimal.sol"; +import "./libraries/UQ104x104.sol"; import "./token/ERC20.sol"; import "./token/SafeTransfer.sol"; @@ -12,6 +12,7 @@ import "./token/SafeTransfer.sol"; contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTransfer { using SafeMath128 for uint128; using SafeMath256 for uint256; + using UQ104x104 for uint208; struct TokenData { uint128 token0; @@ -24,9 +25,9 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran TokenData private reserves; - 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; + uint240 private priceToken0Accumulated; + uint240 private priceToken1Accumulated; + uint32 private blockNumberLast; bool private notEntered = true; modifier lock() { @@ -87,22 +88,18 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran uint128 blocksElapsed = (block.number - blockNumberLast).downcast128(); // 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); + uint208 priceToken0 = UQ104x104.encode(reserves.token0).qdiv(UQ104x104.encode(reserves.token1)); + uint208 priceToken1 = UQ104x104.encode(reserves.token1).qdiv(UQ104x104.encode(reserves.token0)); - // 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); - - // add the accumulated prices to the global accumulators + // multiply these prices by the number of elapsed blocks and add to the accumulators return ( - MOCK_Decimal.add(accumulatedPriceToken0, priceToken0Accumulated).data, - MOCK_Decimal.add(accumulatedPriceToken1, priceToken1Accumulated).data + priceToken0Accumulated + (uint240(priceToken0) * blocksElapsed), + priceToken1Accumulated + (uint240(priceToken1) * blocksElapsed) ); } else { return ( - accumulatedPriceToken0.data, - accumulatedPriceToken1.data + priceToken0Accumulated, + priceToken1Accumulated ); } } @@ -125,24 +122,21 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran // if any blocks have gone by since the last time this function was called, we have to update if (block.number > blockNumberLast) { // make sure that this isn't the first time this function is being called + uint32 blockNumber = block.number.downcast32(); if (blockNumberLast > 0) { - uint128 blocksElapsed = (block.number - blockNumberLast).downcast128(); + uint32 blocksElapsed = blockNumber - blockNumberLast; // 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); + uint208 priceToken0 = UQ104x104.encode(reserves.token0).qdiv(UQ104x104.encode(reserves.token1)); + uint208 priceToken1 = UQ104x104.encode(reserves.token1).qdiv(UQ104x104.encode(reserves.token0)); - // 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); - - // add these incremental accumulated prices to the global accumulators - accumulatedPriceToken0 = MOCK_Decimal.add(accumulatedPriceToken0, priceToken0Accumulated); - accumulatedPriceToken1 = MOCK_Decimal.add(accumulatedPriceToken1, priceToken1Accumulated); + // multiply these prices by the number of elapsed blocks and add to the accumulators + priceToken0Accumulated = priceToken0Accumulated + (uint240(priceToken0) * blocksElapsed); + priceToken1Accumulated = priceToken1Accumulated + (uint240(priceToken1) * blocksElapsed); } // update the last block number - blockNumberLast = block.number; + blockNumberLast = blockNumber; } // update reserves diff --git a/contracts/libraries/MOCK_Decimal.sol b/contracts/libraries/MOCK_Decimal.sol deleted file mode 100644 index 17c16c8..0000000 --- a/contracts/libraries/MOCK_Decimal.sol +++ /dev/null @@ -1,20 +0,0 @@ -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/SafeMath256.sol b/contracts/libraries/SafeMath256.sol index 846c07f..2e7ea2d 100644 --- a/contracts/libraries/SafeMath256.sol +++ b/contracts/libraries/SafeMath256.sol @@ -15,4 +15,9 @@ library SafeMath256 { require(y <= uint128(-1), "downcast-128-overflow"); z = uint128(y); } + + function downcast32(uint256 y) internal pure returns (uint32 z) { + require(y <= uint32(-1), "downcast-32-overflow"); + z = uint32(y); + } } diff --git a/contracts/libraries/UQ104x104.sol b/contracts/libraries/UQ104x104.sol new file mode 100644 index 0000000..0df0926 --- /dev/null +++ b/contracts/libraries/UQ104x104.sol @@ -0,0 +1,17 @@ +pragma solidity 0.5.12; + +// TODO this whole library is basically just a mock at the moment +library UQ104x104 { + uint208 constant Q104 = uint104(-1); + + function encode(uint128 y) internal pure returns (uint208 z) { + // require(y <= Q104, "encode-overflow"); + z = y * Q104; + } + function qmul(uint208 x, uint208 y) internal pure returns (uint208 z) { + z = x * y / Q104; + } + function qdiv(uint208 x, uint208 y) internal pure returns (uint208 z) { + z = x / y; + } +}