@0x/contracts-utils: Roll back additions to LibFractions.

`@0x/contracts-dev-utils`: Add `D18` library for working with base-10, 18-digit decimals.
`@0x/contracts-dev-utils`: Use `D18` library instead of `LibFractions` in `LibDydxBalance`.
This commit is contained in:
Lawrence Forman 2020-02-05 01:49:47 -05:00
parent 9b3781abf1
commit 865a253eb5
10 changed files with 278 additions and 246 deletions

View File

@ -22,6 +22,10 @@
{ {
"note": "Add `DydxBridge` order validation", "note": "Add `DydxBridge` order validation",
"pr": 2466 "pr": 2466
},
{
"note": "Add `D18` library for working with base-10, 18-precision decimals",
"pr": 2466
} }
], ],
"timestamp": 1581204851 "timestamp": 1581204851

View File

@ -375,7 +375,7 @@ contract AssetBalance is
(, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData); (, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData);
if (bridgeAddress == dydxBridgeAddress) { if (bridgeAddress == dydxBridgeAddress) {
return ( return (
LibDydxBalance.getDydxMakerBalance(order, dydxBridgeAddress), LibDydxBalance.getDydxMakerBalance(order, _getDydxAddress()),
_MAX_UINT256 _MAX_UINT256
); );
} }

View File

@ -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"
);
}
}

View File

@ -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/IDydxBridge.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.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-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.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 "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./LibAssetData.sol"; import "./LibAssetData.sol";
import "./D18.sol";
// solhint-disable separate-by-one-line-in-contract
library LibDydxBalance { library LibDydxBalance {
using LibBytes for bytes; 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 /// @dev Structure that holds all pertinent info needed to perform a balance
/// check. /// check.
@ -82,7 +68,7 @@ library LibDydxBalance {
} }
// If the rate we withdraw maker tokens is < 1, the asset proxy will // If the rate we withdraw maker tokens is < 1, the asset proxy will
// throw because we will always transfer less maker tokens than asked. // 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; return 0;
} }
// The maker balance is the smaller of: // The maker balance is the smaller of:
@ -153,7 +139,7 @@ library LibDydxBalance {
function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info) function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info)
internal internal
pure pure
returns (Fraction memory makerTokenWithdrawRate) returns (int256 makerTokenWithdrawRate)
{ {
// The last action is always a withdraw for the maker token. // The last action is always a withdraw for the maker token.
IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1];
@ -169,7 +155,7 @@ library LibDydxBalance {
{ {
depositableMakerAmount = uint256(-1); depositableMakerAmount = uint256(-1);
// The conversion rate from maker -> taker. // The conversion rate from maker -> taker.
Fraction memory makerToTakerRate = Fraction( int256 makerToTakerRate = D18.div(
info.takerAssetAmount, info.takerAssetAmount,
info.makerAssetAmount info.makerAssetAmount
); );
@ -180,22 +166,17 @@ library LibDydxBalance {
if (action.actionType != IDydxBridge.BridgeActionType.Deposit) { if (action.actionType != IDydxBridge.BridgeActionType.Deposit) {
continue; continue;
} }
Fraction memory depositRate = _getActionRate(action); int256 depositRate = _getActionRate(action);
// Taker tokens will be transferred to the maker for every fill, so // Taker tokens will be transferred to the maker for every fill, so
// we reduce the effective deposit rate if we're depositing the taker // we reduce the effective deposit rate if we're depositing the taker
// token. // token.
address depositToken = info.dydx.getMarketTokenAddress(action.marketId); address depositToken = info.dydx.getMarketTokenAddress(action.marketId);
if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) { if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) {
// `depositRate = max(0, depositRate - makerToTakerRate)` depositRate = D18.sub(depositRate, makerToTakerRate);
if (_ltf(makerToTakerRate, depositRate)) {
depositRate = _subf(depositRate, makerToTakerRate);
} else {
depositRate = Fraction(0, 1);
}
} }
// If the deposit rate is > 0, we are limited by the transferrable // If the deposit rate is > 0, we are limited by the transferrable
// token balance of the maker. // token balance of the maker.
if (_gtf(depositRate, Fraction(0, 1))) { if (depositRate > 0) {
uint256 supply = _getTransferabeTokenAmount( uint256 supply = _getTransferabeTokenAmount(
depositToken, depositToken,
info.makerAddress, info.makerAddress,
@ -203,11 +184,7 @@ library LibDydxBalance {
); );
depositableMakerAmount = LibSafeMath.min256( depositableMakerAmount = LibSafeMath.min256(
depositableMakerAmount, depositableMakerAmount,
LibMath.getPartialAmountFloor( uint256(D18.div(supply, depositRate))
depositRate.d,
depositRate.n,
supply
)
); );
} }
} }
@ -225,15 +202,13 @@ library LibDydxBalance {
assert(info.actions.length >= 1); assert(info.actions.length >= 1);
IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1];
assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw); assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw);
Fraction memory minCr = _getMinimumCollateralizationRatio(info.dydx); int256 minCr = _getMinimumCollateralizationRatio(info.dydx);
// CR < 1 will cause math underflows.
require(_gtef(minCr, Fraction(1, 1)), "DevUtils/MIN_CR_MUST_BE_GTE_ONE");
// Loop through the accounts. // Loop through the accounts.
for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) { for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) {
(uint256 supplyValue, uint256 borrowValue) = (uint256 supplyValue, uint256 borrowValue) =
_getAccountValues(info, info.accounts[accountIdx]); _getAccountValues(info, info.accounts[accountIdx]);
// All accounts must currently be solvent. // All accounts must currently be solvent.
if (borrowValue != 0 && _ltf(Fraction(supplyValue, borrowValue), minCr)) { if (borrowValue != 0 && D18.div(supplyValue, borrowValue) < minCr) {
return 0; return 0;
} }
// If this is the same account used to in the withdraw/borrow action, // 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 // Compute the deposit/collateralization rate, which is the rate at
// which (USD) value is added to the account across all markets. // 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) { for (uint256 i = 0; i < info.actions.length - 1; ++i) {
IDydxBridge.BridgeAction memory deposit = info.actions[i]; IDydxBridge.BridgeAction memory deposit = info.actions[i];
assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit); assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit);
if (deposit.accountIdx == accountIdx) { if (deposit.accountIdx == accountIdx) {
dd = _addf( dd = D18.add(
dd, dd,
_toQuoteValue( _toQuoteValue(
info.dydx, info.dydx,
@ -260,14 +235,14 @@ library LibDydxBalance {
} }
// Compute the borrow/withdraw rate, which is the rate at which // Compute the borrow/withdraw rate, which is the rate at which
// (USD) value is deducted from the account. // (USD) value is deducted from the account.
Fraction memory db = _toQuoteValue( int256 db = _toQuoteValue(
info.dydx, info.dydx,
withdraw.marketId, withdraw.marketId,
_getActionRate(withdraw) _getActionRate(withdraw)
); );
// If the deposit to withdraw ratio is >= the minimum collateralization // If the deposit to withdraw ratio is >= the minimum collateralization
// rate, then we will never become insolvent at these prices. // rate, then we will never become insolvent at these prices.
if (_gtef(_divf(dd, db), minCr)) { if (D18.div(dd, db) >= minCr) {
continue; continue;
} }
// The collateralization ratio for this account, parameterized by // The collateralization ratio for this account, parameterized by
@ -275,18 +250,13 @@ library LibDydxBalance {
// `cr = (supplyValue + t * dd) / (borrowValue + t * db)` // `cr = (supplyValue + t * dd) / (borrowValue + t * db)`
// Solving for `t` gives us: // Solving for `t` gives us:
// `t = (supplyValue - cr * borrowValue) / (cr * db - dd)` // `t = (supplyValue - cr * borrowValue) / (cr * db - dd)`
// TODO(dorothy-zbornak): It'll also revert when getting extremely int256 t = D18.div(
// close to the minimum collateralization ratio. D18.sub(supplyValue, D18.mul(minCr, borrowValue)),
Fraction memory t = _divf( D18.sub(D18.mul(minCr, db), dd)
_subf(
Fraction(supplyValue, DYDX_UNITS_BASE),
_mulf(minCr, Fraction(borrowValue, DYDX_UNITS_BASE))
),
_subf(_mulf(minCr, db), dd)
); );
solventMakerAmount = LibSafeMath.min256( solventMakerAmount = LibSafeMath.min256(
solventMakerAmount, solventMakerAmount,
t.n.safeDiv(t.d) uint256(D18.clip(t))
); );
} }
} }
@ -322,15 +292,13 @@ library LibDydxBalance {
function _getActionRate(IDydxBridge.BridgeAction memory action) function _getActionRate(IDydxBridge.BridgeAction memory action)
private private
pure pure
returns (Fraction memory rate) returns (int256 rate)
{ {
rate = action.conversionRateDenominator == 0 rate = action.conversionRateDenominator == 0
? Fraction(1, 1) ? D18.one()
: _normalizef( : D18.div(
Fraction(
action.conversionRateNumerator, action.conversionRateNumerator,
action.conversionRateDenominator action.conversionRateDenominator
)
); );
} }
@ -340,35 +308,24 @@ library LibDydxBalance {
function _getMinimumCollateralizationRatio(IDydx dydx) function _getMinimumCollateralizationRatio(IDydx dydx)
private private
view view
returns (Fraction memory ratio) returns (int256 ratio)
{ {
IDydx.RiskParams memory riskParams = dydx.getRiskParams(); IDydx.RiskParams memory riskParams = dydx.getRiskParams();
return _normalizef( return D18.toSigned(riskParams.marginRatio.value);
Fraction(
riskParams.marginRatio.value,
DYDX_UNITS_BASE
)
);
} }
/// @dev Get the quote (USD) value of a rate within a market. /// @dev Get the quote (USD) value of a rate within a market.
/// @param dydx The Dydx interface. /// @param dydx The Dydx interface.
/// @param marketId Dydx market ID. /// @param marketId Dydx market ID.
/// @param rate Rate to scale by price. /// @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 private
view view
returns (Fraction memory quotedRate) returns (int256 quotedRate)
{ {
IDydx.Price memory price = dydx.getMarketPrice(marketId); IDydx.Price memory price = dydx.getMarketPrice(marketId);
uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId)); uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId));
return _mulf( return D18.mul(D18.div(price.value, 10 ** uint256(tokenDecimals)), rate);
Fraction(
price.value,
10 ** uint256(DYDX_UNITS_DECIMALS + tokenDecimals)
),
rate
);
} }
/// @dev Get the total supply and borrow values for an account across all markets. /// @dev Get the total supply and borrow values for an account across all markets.
@ -406,78 +363,4 @@ library LibDydxBalance {
LibERC20Token.balanceOf(tokenAddress, owner) 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);
}
} }

View File

@ -28,7 +28,7 @@
}, },
"config": { "config": {
"publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder", "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." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {

View File

@ -7,6 +7,7 @@ import { ContractArtifact } from 'ethereum-types';
import * as Addresses from '../test/generated-artifacts/Addresses.json'; import * as Addresses from '../test/generated-artifacts/Addresses.json';
import * as AssetBalance from '../test/generated-artifacts/AssetBalance.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 DevUtils from '../test/generated-artifacts/DevUtils.json';
import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json'; import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json';
import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.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 = { export const artifacts = {
Addresses: Addresses as ContractArtifact, Addresses: Addresses as ContractArtifact,
AssetBalance: AssetBalance as ContractArtifact, AssetBalance: AssetBalance as ContractArtifact,
D18: D18 as ContractArtifact,
DevUtils: DevUtils as ContractArtifact, DevUtils: DevUtils as ContractArtifact,
EthBalanceChecker: EthBalanceChecker as ContractArtifact, EthBalanceChecker: EthBalanceChecker as ContractArtifact,
ExternalFunctions: ExternalFunctions as ContractArtifact, ExternalFunctions: ExternalFunctions as ContractArtifact,

View File

@ -5,6 +5,7 @@
*/ */
export * from '../test/generated-wrappers/addresses'; export * from '../test/generated-wrappers/addresses';
export * from '../test/generated-wrappers/asset_balance'; 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/dev_utils';
export * from '../test/generated-wrappers/eth_balance_checker'; export * from '../test/generated-wrappers/eth_balance_checker';
export * from '../test/generated-wrappers/external_functions'; export * from '../test/generated-wrappers/external_functions';

View File

@ -10,6 +10,7 @@
"generated-artifacts/LibTransactionDecoder.json", "generated-artifacts/LibTransactionDecoder.json",
"test/generated-artifacts/Addresses.json", "test/generated-artifacts/Addresses.json",
"test/generated-artifacts/AssetBalance.json", "test/generated-artifacts/AssetBalance.json",
"test/generated-artifacts/D18.json",
"test/generated-artifacts/DevUtils.json", "test/generated-artifacts/DevUtils.json",
"test/generated-artifacts/EthBalanceChecker.json", "test/generated-artifacts/EthBalanceChecker.json",
"test/generated-artifacts/ExternalFunctions.json", "test/generated-artifacts/ExternalFunctions.json",

View File

@ -37,10 +37,6 @@
{ {
"note": "Export `EvmBytecodeOutputLinkReferences` type.", "note": "Export `EvmBytecodeOutputLinkReferences` type.",
"pr": 2462 "pr": 2462
},
{
"note": "Add more functions to `LibFractions`.",
"pr": 2466
} }
], ],
"timestamp": 1580811564 "timestamp": 1580811564

View File

@ -37,101 +37,7 @@ library LibFractions {
.safeMul(d2) .safeMul(d2)
.safeAdd(n2.safeMul(d1)); .safeAdd(n2.safeMul(d1));
denominator = d1.safeMul(d2); denominator = d1.safeMul(d2);
return normalize(numerator, denominator); return (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;
} }
/// @dev Rescales a fraction to prevent overflows during addition if either /// @dev Rescales a fraction to prevent overflows during addition if either