diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index 0e8f288e99..03fa998383 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -22,6 +22,10 @@ { "note": "Add `DydxBridge` order validation", "pr": 2466 + }, + { + "note": "Add `D18` library for working with base-10, 18-precision decimals", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/contracts/dev-utils/contracts/src/AssetBalance.sol b/contracts/dev-utils/contracts/src/AssetBalance.sol index 45172c757e..ca4dd57a6f 100644 --- a/contracts/dev-utils/contracts/src/AssetBalance.sol +++ b/contracts/dev-utils/contracts/src/AssetBalance.sol @@ -375,7 +375,7 @@ contract AssetBalance is (, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData); if (bridgeAddress == dydxBridgeAddress) { return ( - LibDydxBalance.getDydxMakerBalance(order, dydxBridgeAddress), + LibDydxBalance.getDydxMakerBalance(order, _getDydxAddress()), _MAX_UINT256 ); } diff --git a/contracts/dev-utils/contracts/src/D18.sol b/contracts/dev-utils/contracts/src/D18.sol new file mode 100644 index 0000000000..f491976186 --- /dev/null +++ b/contracts/dev-utils/contracts/src/D18.sol @@ -0,0 +1,239 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.16; + + +/// @dev A library for working with 18 digit, base 10 decimals. +library D18 { + + /// @dev Decimal places for dydx value quantities. + uint256 private constant PRECISION = 18; + /// @dev 1.0 in base-18 decimal. + int256 private constant DECIMAL_ONE = int256(10 ** PRECISION); + /// @dev Minimum signed integer value. + int256 private constant MIN_INT256_VALUE = int256(0x8000000000000000000000000000000000000000000000000000000000000000); + + /// @dev Return `1.0` + function one() + internal + pure + returns (int256 r) + { + r = DECIMAL_ONE; + } + + /// @dev Add two decimals. + function add(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _add(a, b); + } + + /// @dev Add two decimals. + function add(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), b); + } + + /// @dev Add two decimals. + function add(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), int256(b)); + } + + /// @dev Subract two decimals. + function sub(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _add(a, -b); + } + + /// @dev Subract two decimals. + function sub(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), -b); + } + + /// @dev Subract two decimals. + function sub(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), -int256(b)); + } + + /// @dev Multiply two decimals. + function mul(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _div(_mul(a, b), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), b), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(a, int256(b)), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), int256(b)), DECIMAL_ONE); + } + + /// @dev Divide two decimals. + function div(int256 a, int256 b) + internal + pure + returns (int256 r) + { + require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW"); + r = _div(_mul(a, DECIMAL_ONE), b); + } + + /// @dev Divide two decimals. + function div(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), DECIMAL_ONE), b); + } + + /// @dev Divide two decimals. + function div(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(a, DECIMAL_ONE), int256(b)); + } + + /// @dev Divide two decimals. + function div(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(uint256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), DECIMAL_ONE), int256(b)); + } + + /// @dev Safely convert an unsigned integer into a signed integer. + function toSigned(uint256 a) + internal + pure + returns (int256 r) + { + require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = int256(a); + } + + /// @dev Clip a signed value to be positive. + function clip(int256 a) + internal + pure + returns (int256 r) + { + r = a < 0 ? 0 : a; + } + + /// @dev Safely multiply two signed integers. + function _mul(int256 a, int256 b) + private + pure + returns (int256 r) + { + if (a == 0 || b == 0) { + return 0; + } + r = a * b; + require(r / a == b && r / b == a, "D18/DECIMAL_MUL_OVERFLOW"); + return r; + } + + /// @dev Safely divide two signed integers. + function _div(int256 a, int256 b) + private + pure + returns (int256 r) + { + require(b != 0, "D18/DECIMAL_DIVISION_BY_ZERO"); + require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW"); + r = a / b; + } + + /// @dev Safely add two signed integers. + function _add(int256 a, int256 b) + private + pure + returns (int256 r) + { + r = a + b; + require( + !((a < 0 && b < 0 && r > a) || (a > 0 && b > 0 && r < a)), + "D18/DECIMAL_MUL_OVERFLOW" + ); + } + +} diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index dac436f08d..bf061ac5fa 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -23,30 +23,16 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydxBridge.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/LibFractions.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "./LibAssetData.sol"; +import "./D18.sol"; -// solhint-disable separate-by-one-line-in-contract library LibDydxBalance { using LibBytes for bytes; - using LibSafeMath for uint256; - - /// @dev Decimal places for dydx value quantities. - uint256 private constant DYDX_UNITS_DECIMALS = 18; - /// @dev Base units for dydx value quantities. - uint256 private constant DYDX_UNITS_BASE = 10 ** DYDX_UNITS_DECIMALS; - - /// @dev A fraction/rate. - struct Fraction { - uint256 n; - uint256 d; - } /// @dev Structure that holds all pertinent info needed to perform a balance /// check. @@ -82,7 +68,7 @@ library LibDydxBalance { } // If the rate we withdraw maker tokens is < 1, the asset proxy will // throw because we will always transfer less maker tokens than asked. - if (_ltf(_getMakerTokenWithdrawRate(info), Fraction(1, 1))) { + if (_getMakerTokenWithdrawRate(info) < D18.one()) { return 0; } // The maker balance is the smaller of: @@ -153,7 +139,7 @@ library LibDydxBalance { function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info) internal pure - returns (Fraction memory makerTokenWithdrawRate) + returns (int256 makerTokenWithdrawRate) { // The last action is always a withdraw for the maker token. IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; @@ -169,7 +155,7 @@ library LibDydxBalance { { depositableMakerAmount = uint256(-1); // The conversion rate from maker -> taker. - Fraction memory makerToTakerRate = Fraction( + int256 makerToTakerRate = D18.div( info.takerAssetAmount, info.makerAssetAmount ); @@ -180,22 +166,17 @@ library LibDydxBalance { if (action.actionType != IDydxBridge.BridgeActionType.Deposit) { continue; } - Fraction memory depositRate = _getActionRate(action); + int256 depositRate = _getActionRate(action); // Taker tokens will be transferred to the maker for every fill, so // we reduce the effective deposit rate if we're depositing the taker // token. address depositToken = info.dydx.getMarketTokenAddress(action.marketId); if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) { - // `depositRate = max(0, depositRate - makerToTakerRate)` - if (_ltf(makerToTakerRate, depositRate)) { - depositRate = _subf(depositRate, makerToTakerRate); - } else { - depositRate = Fraction(0, 1); - } + depositRate = D18.sub(depositRate, makerToTakerRate); } // If the deposit rate is > 0, we are limited by the transferrable // token balance of the maker. - if (_gtf(depositRate, Fraction(0, 1))) { + if (depositRate > 0) { uint256 supply = _getTransferabeTokenAmount( depositToken, info.makerAddress, @@ -203,11 +184,7 @@ library LibDydxBalance { ); depositableMakerAmount = LibSafeMath.min256( depositableMakerAmount, - LibMath.getPartialAmountFloor( - depositRate.d, - depositRate.n, - supply - ) + uint256(D18.div(supply, depositRate)) ); } } @@ -225,15 +202,13 @@ library LibDydxBalance { assert(info.actions.length >= 1); IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw); - Fraction memory minCr = _getMinimumCollateralizationRatio(info.dydx); - // CR < 1 will cause math underflows. - require(_gtef(minCr, Fraction(1, 1)), "DevUtils/MIN_CR_MUST_BE_GTE_ONE"); + int256 minCr = _getMinimumCollateralizationRatio(info.dydx); // Loop through the accounts. for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) { (uint256 supplyValue, uint256 borrowValue) = _getAccountValues(info, info.accounts[accountIdx]); // All accounts must currently be solvent. - if (borrowValue != 0 && _ltf(Fraction(supplyValue, borrowValue), minCr)) { + if (borrowValue != 0 && D18.div(supplyValue, borrowValue) < minCr) { return 0; } // If this is the same account used to in the withdraw/borrow action, @@ -243,12 +218,12 @@ library LibDydxBalance { } // Compute the deposit/collateralization rate, which is the rate at // which (USD) value is added to the account across all markets. - Fraction memory dd = Fraction(0, 1); + int256 dd = 0; for (uint256 i = 0; i < info.actions.length - 1; ++i) { IDydxBridge.BridgeAction memory deposit = info.actions[i]; assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit); if (deposit.accountIdx == accountIdx) { - dd = _addf( + dd = D18.add( dd, _toQuoteValue( info.dydx, @@ -260,14 +235,14 @@ library LibDydxBalance { } // Compute the borrow/withdraw rate, which is the rate at which // (USD) value is deducted from the account. - Fraction memory db = _toQuoteValue( + int256 db = _toQuoteValue( info.dydx, withdraw.marketId, _getActionRate(withdraw) ); // If the deposit to withdraw ratio is >= the minimum collateralization // rate, then we will never become insolvent at these prices. - if (_gtef(_divf(dd, db), minCr)) { + if (D18.div(dd, db) >= minCr) { continue; } // The collateralization ratio for this account, parameterized by @@ -275,18 +250,13 @@ library LibDydxBalance { // `cr = (supplyValue + t * dd) / (borrowValue + t * db)` // Solving for `t` gives us: // `t = (supplyValue - cr * borrowValue) / (cr * db - dd)` - // TODO(dorothy-zbornak): It'll also revert when getting extremely - // close to the minimum collateralization ratio. - Fraction memory t = _divf( - _subf( - Fraction(supplyValue, DYDX_UNITS_BASE), - _mulf(minCr, Fraction(borrowValue, DYDX_UNITS_BASE)) - ), - _subf(_mulf(minCr, db), dd) + int256 t = D18.div( + D18.sub(supplyValue, D18.mul(minCr, borrowValue)), + D18.sub(D18.mul(minCr, db), dd) ); solventMakerAmount = LibSafeMath.min256( solventMakerAmount, - t.n.safeDiv(t.d) + uint256(D18.clip(t)) ); } } @@ -322,15 +292,13 @@ library LibDydxBalance { function _getActionRate(IDydxBridge.BridgeAction memory action) private pure - returns (Fraction memory rate) + returns (int256 rate) { rate = action.conversionRateDenominator == 0 - ? Fraction(1, 1) - : _normalizef( - Fraction( - action.conversionRateNumerator, - action.conversionRateDenominator - ) + ? D18.one() + : D18.div( + action.conversionRateNumerator, + action.conversionRateDenominator ); } @@ -340,35 +308,24 @@ library LibDydxBalance { function _getMinimumCollateralizationRatio(IDydx dydx) private view - returns (Fraction memory ratio) + returns (int256 ratio) { IDydx.RiskParams memory riskParams = dydx.getRiskParams(); - return _normalizef( - Fraction( - riskParams.marginRatio.value, - DYDX_UNITS_BASE - ) - ); + return D18.toSigned(riskParams.marginRatio.value); } /// @dev Get the quote (USD) value of a rate within a market. /// @param dydx The Dydx interface. /// @param marketId Dydx market ID. /// @param rate Rate to scale by price. - function _toQuoteValue(IDydx dydx, uint256 marketId, Fraction memory rate) + function _toQuoteValue(IDydx dydx, uint256 marketId, int256 rate) private view - returns (Fraction memory quotedRate) + returns (int256 quotedRate) { IDydx.Price memory price = dydx.getMarketPrice(marketId); uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId)); - return _mulf( - Fraction( - price.value, - 10 ** uint256(DYDX_UNITS_DECIMALS + tokenDecimals) - ), - rate - ); + return D18.mul(D18.div(price.value, 10 ** uint256(tokenDecimals)), rate); } /// @dev Get the total supply and borrow values for an account across all markets. @@ -406,78 +363,4 @@ library LibDydxBalance { LibERC20Token.balanceOf(tokenAddress, owner) ); } - - /*** Fraction helpers ***/ - - /// @dev Check if `a < b`. - function _ltf(Fraction memory a, Fraction memory b) - private - pure - returns (bool isLessThan) - { - return LibFractions.cmp(a.n, a.d, b.n, b.d) == -1; - } - - /// @dev Check if `a > b`. - function _gtf(Fraction memory a, Fraction memory b) - private - pure - returns (bool isGreaterThan) - { - return LibFractions.cmp(a.n, a.d, b.n, b.d) == 1; - } - - /// @dev Check if `a >= b`. - function _gtef(Fraction memory a, Fraction memory b) - private - pure - returns (bool isGreaterThanOrEqual) - { - return !_ltf(a, b); - } - - /// @dev Compute `a + b`. - function _addf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.add(a.n, a.d, b.n, b.d); - } - - /// @dev Compute `a - b`. - function _subf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.sub(a.n, a.d, b.n, b.d); - } - - /// @dev Compute `a * b`. - function _mulf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.mul(a.n, a.d, b.n, b.d); - } - - /// @dev Compute `a / b`. - function _divf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.mul(a.n, a.d, b.d, b.n); - } - - /// @dev Normalize a fraction to prevent arithmetic overflows. - function _normalizef(Fraction memory f) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.normalize(f.n, f.d); - } } diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index b3cf58b3ee..3dd5b99b23 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -28,7 +28,7 @@ }, "config": { "publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder", - "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", + "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|D18|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/test/artifacts.ts b/contracts/dev-utils/test/artifacts.ts index e4c1802bc0..a4ff20758e 100644 --- a/contracts/dev-utils/test/artifacts.ts +++ b/contracts/dev-utils/test/artifacts.ts @@ -7,6 +7,7 @@ import { ContractArtifact } from 'ethereum-types'; import * as Addresses from '../test/generated-artifacts/Addresses.json'; import * as AssetBalance from '../test/generated-artifacts/AssetBalance.json'; +import * as D18 from '../test/generated-artifacts/D18.json'; import * as DevUtils from '../test/generated-artifacts/DevUtils.json'; import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json'; import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json'; @@ -21,6 +22,7 @@ import * as TestLibDydxBalance from '../test/generated-artifacts/TestLibDydxBala export const artifacts = { Addresses: Addresses as ContractArtifact, AssetBalance: AssetBalance as ContractArtifact, + D18: D18 as ContractArtifact, DevUtils: DevUtils as ContractArtifact, EthBalanceChecker: EthBalanceChecker as ContractArtifact, ExternalFunctions: ExternalFunctions as ContractArtifact, diff --git a/contracts/dev-utils/test/wrappers.ts b/contracts/dev-utils/test/wrappers.ts index 01f291fa09..587e4c6281 100644 --- a/contracts/dev-utils/test/wrappers.ts +++ b/contracts/dev-utils/test/wrappers.ts @@ -5,6 +5,7 @@ */ export * from '../test/generated-wrappers/addresses'; export * from '../test/generated-wrappers/asset_balance'; +export * from '../test/generated-wrappers/d18'; export * from '../test/generated-wrappers/dev_utils'; export * from '../test/generated-wrappers/eth_balance_checker'; export * from '../test/generated-wrappers/external_functions'; diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index eccc9bf633..b2b229e0c7 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -10,6 +10,7 @@ "generated-artifacts/LibTransactionDecoder.json", "test/generated-artifacts/Addresses.json", "test/generated-artifacts/AssetBalance.json", + "test/generated-artifacts/D18.json", "test/generated-artifacts/DevUtils.json", "test/generated-artifacts/EthBalanceChecker.json", "test/generated-artifacts/ExternalFunctions.json", diff --git a/contracts/utils/CHANGELOG.json b/contracts/utils/CHANGELOG.json index 3551b3a3d1..251907e9c5 100644 --- a/contracts/utils/CHANGELOG.json +++ b/contracts/utils/CHANGELOG.json @@ -37,10 +37,6 @@ { "note": "Export `EvmBytecodeOutputLinkReferences` type.", "pr": 2462 - }, - { - "note": "Add more functions to `LibFractions`.", - "pr": 2466 } ], "timestamp": 1580811564 diff --git a/contracts/utils/contracts/src/LibFractions.sol b/contracts/utils/contracts/src/LibFractions.sol index 1582733988..f5a05706c6 100644 --- a/contracts/utils/contracts/src/LibFractions.sol +++ b/contracts/utils/contracts/src/LibFractions.sol @@ -37,101 +37,7 @@ library LibFractions { .safeMul(d2) .safeAdd(n2.safeMul(d1)); denominator = d1.safeMul(d2); - return normalize(numerator, denominator); - } - - /// @dev Safely subracts two fractions `n1/d1 - n2/d2` - /// @param n1 numerator of `1` - /// @param d1 denominator of `1` - /// @param n2 numerator of `2` - /// @param d2 denominator of `2` - /// @return numerator Numerator of sum - /// @return denominator Denominator of sum - function sub( - uint256 n1, - uint256 d1, - uint256 n2, - uint256 d2 - ) - internal - pure - returns ( - uint256 numerator, - uint256 denominator - ) - { - if (n2 == 0) { - return (numerator = n1, denominator = d1); - } - numerator = n1 - .safeMul(d2) - .safeSub(n2.safeMul(d1)); - denominator = d1.safeMul(d2); - return normalize(numerator, denominator); - } - - /// @dev Multiply two fractions. - /// @param n1 numerator of `1` - /// @param d1 denominator of `1` - /// @param n2 numerator of `2` - /// @param d2 denominator of `2` - /// @return numerator numerator of product. - /// @return denominator numerator of product. - function mul( - uint256 n1, - uint256 d1, - uint256 n2, - uint256 d2 - ) - internal - pure - returns ( - uint256 numerator, - uint256 denominator - ) - { - return normalize(n1.safeMul(n2), d1.safeMul(d2)); - } - - /// @dev Compares two fractions. - /// @param n1 numerator of `1` - /// @param d1 denominator of `1` - /// @param n2 numerator of `2` - /// @param d2 denominator of `2` - /// @return compareResult - /// `-1` if `n1/d1 < n2/d2`. - /// `0` if `n1/d1 == n2/d2`. - /// `1` if `n1/d1 > n2/d2`. - function cmp( - uint256 n1, - uint256 d1, - uint256 n2, - uint256 d2 - ) - internal - pure - returns (int8 compareResult) - { - // Handle infinities. - if (d1 == 0) { - if (d2 == 0) { - return 0; - } - return 1; - } else { - if (d2 == 0) { - return -1; - } - } - uint256 nd1 = n1.safeMul(d2); - uint256 nd2 = n2.safeMul(d1); - if (nd1 > nd2) { - return 1; - } - if (nd1 < nd2) { - return -1; - } - return 0; + return (numerator, denominator); } /// @dev Rescales a fraction to prevent overflows during addition if either