Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dist: bionic
language: node_js
cache: yarn
node_js:
- lts/erbium
install:
- yarn
script:
- yarn coverage
42 changes: 42 additions & 0 deletions ABOUT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# FixidityLib
An overflow-protected fixed-point arithmetic library for Solidity.

## Introduction
FixidityLib provide fixed point arithmetic for Solidity. This is achieved by
using int256 as the type throughout the library and designating a number of
digits in each int256 for holding the fractional part. This is equivalent to
displacing the comma in a non-integer number a fixed number of positions to the
left. All the arithmetic operations that have been implemented maintain
constant the number of digits to the left of the comma that are represented.

The main arithmetic operations currently supported are addition, subtraction,
multiplication, division and logarithms. In a near future exponentials and
square roots might be supported.

In addition to fixed point arithmetic operations, FixidityLib is fully
protected against overflow. Any operation that causes an overflow to happen
will revert.

A number of constants have been provided that identify the safe
limits for operation. When fixed-point arithmetic operations are done with
values between zero and a limit there will be no overflows and therefore the
functions will never revert. It would be then useful in some cases to use
those limits to reject user inputs with an informative message when those
inputs might cause erratic behaviour. With careful consideration it is
possible to perform operations with values that are beyond the limits, but
doing so must only be done with appropriate knowledge of range of each
operator.

FixidityLib currently assumes 24 digits as the desired size for the decimal
part. To change this to a different value the constants need to be adjusted.
The formulas used to calculate the constants have been provided to facilitate
this. All other functions will work as long as the constants are consistent.

An extensive collection of tests was created to prove the robustness of
FixidityLib, and they should be run whenever a change in the digits is done.

Currently FixidityLib doesn't use its own type, so it is up to the user to
remember whether a given int256 is a FixidityLib fixed point number or not,
and to take care to use the toFixed() and toUint() functions accordingly
to create fixed point numbers and convert them back to integers.

160 changes: 106 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,56 @@
# FixidityLib
An overflow-protected fixed-point arithmetic library for Solidity.

## Introduction
FixidityLib provide fixed point arithmetic for Solidity. This is achieved by
using int256 as the type throughout the library and designating a number of
digits in each int256 for holding the fractional part. This is equivalent to
displacing the comma in a non-integer number a fixed number of positions to the
left. All the arithmetic operations that have been implemented maintain
constant the number of digits to the left of the comma that are represented.

The main arithmetic operations currently supported are addition, subtraction,
multiplication, division and logarithms. In a near future exponentials and
square roots might be supported.

In addition to fixed-point airthmetic operations, FixidityLib is fully
protected against overflow. Any operation that causes an overflow to happen
will revert.

A number of constants have been provided that identify the safe
limits for operation. When fixed-point arithmetic operations are done with
values between zero and a limit there will be no overflows and therefore the
functions will never revert. It would be then useful in some cases to use
those limits to reject user inputs with an informative message when those
inputs might cause erratic behaviour. With careful consideration it is
possible to perform operations with values that are beyond the limits, but
doing so must only be done with appropriate knowledge of range of each
operator.

FixidityLib currently assumes 24 digits as the desired size for the decimal
part. To change this to a different value the constants need to be adjusted.
The formulas used to calculate the constants have been provided to facilitate
this. All other functions will work as long as the constants are consistent.

An extensive collection of tests was created to prove the robustness of
FixidityLib, and they should be run whenever a change in the digits is done.

Currently FixidityLib doesn't use its own type, so it is up to the user to
remember whether a given int256 is a FixidityLibe fixed point number or not,
and to take care to use the newFixed() and fromFixed() functions accordingly
to create fixed point numbers and convert them back to non-integers.
## Testing
`yarn`
In a separate terminal: `yarn start:ganache:local`
`npx truffle compile`
`npx truffle migrate`
`npx truffle test`

## Usage
Fixidity implements fixed point arithmetic by keeping a "virtual comma" in an int256.

With the current implementation that uses 24 digits, 1 in Fixidity is 1 * 10**24.
In a similar manner, 0.5 would be 5 * 10**23.

Fixidity stores fixed point numbers in an int256, and it is up to the developer to remember whether a specific int256 is a Fixidity fixed point number or not.

To create Fixidity fixed point numbers use `toFixed()`.

```
using FixidityLib for uint256;
uint256 x = 1;
int256 xFixed;
xFixed = x.toFixed();
...
```

To convert Fixidity fixed point number back to uint use `toUint()`.
```
...
using FixidityLib for uint256;
using FixidityLib for int256;
uint256 x = 1;
uint256 y = 1;
int256 xFixed = x.toFixed();
int256 yFixed = y.toFixed();
int256 zFixed = x.add(y);
uint256 z = zFixed.toUint();
```

FixidityLib can keep 24 digits, when using `toUint()` only the integer part will be returned.

## Fixed Point Math with Tokens
Ether and the ERC20 implementation use an implicit fixed point representation, although no arithmetic comes bundled up with them.

The `decimals()` field from `ERC20Detailed` states how many digits are in the fractional part of a token. For Ether the fractional part of a token takes 18 digits.

When using Fixidity to operate on wei or `ERC20Detailed` tokens, it is recommended to remember that these already have an implicit fractional part. When converting an `uint` to a fixed point number Fixidity will just add 24 zeros at the end. If the uint contains a number representing a large number of tokens an overflow will be more likely that it needs to be.

When converting an ether wei amount to Fixidity fixed point use `toFixed(18)`. When converting from Fixidity fixed point to we use `toUint(18)`.
When converting an ERC20 wei amount to Fixidity fixed point use `toFixed(token.decimals())`. When converting from Fixidity fixed point to we use `toUint(token.decimals())`.


## FixidityLib.sol
This library implements addition, subtraction,
Expand All @@ -55,7 +67,7 @@ Default: 1000000000000000000000000000000000000

**function mulPrecision() public pure returns(int256)**
The amount of decimals lost on each multiplication operand.
Calcualted as mulPrecision() = sqrt(fixed1)
Calculated as mulPrecision() = sqrt(fixed1)
Default: 1000000000000000000

**function maxInt256() public pure returns(int256)**
Expand Down Expand Up @@ -96,7 +108,7 @@ Maximum value that can be safely used as a multiplication operator.
Divisions where a value is divided by another over this value might overflow,
but not necessarily so.
Calculated as maxFixedMul() = sqrt(maxNewFixed())*fixed1().
Default: 240615969168004511545000000000000000000000000000000000000
Default: 240615969168004511545033772477625056927114980741063

**function maxFixedDiv() public pure returns(int256)**
Maximum value that can be safely used as a dividend.
Expand All @@ -113,34 +125,47 @@ by zero.
Calculated as maxFixedDivisor() = fixed1()*fixed1()
Default: 1000000000000000000000000000000000000000000000000000000000000000000000000

**function newFixed(int256 x)**
Converts an int256 to fixed point units, equivalent to multiplying by
10^digits().

**function fromFixed(int256 x)**
Converts an int256 in the fixed point representation of this library to a non
decimal. All decimal digits will be truncated.

**function convertFixed(int256 x, uint8 _originDigits, uint8 _destinationDigits)**
Converts an int256 which is already in some fixed point representation to a
different fixed precision representation. Both the origin and destination
precisions must be 38 or less digits. Origin values with a precision higher
than the destination precision will be truncated accordingly.

**function newFixed(int256 x, uint8 _originDigits)**
**function toFixed(int256 x)**
Converts an int256 to fixed point units, equivalent to multiplying by
10^digits().

**function toFixed(uint256 x)**
Converts an uint256 to fixed point units, equivalent to multiplying by
10^digits().

**function toFixed(int256 x, uint8 _originDigits)**
Converts an int256 which is already in some fixed point representation to that
of this library. The _originDigits parameter is the precision of x. Values with
a precision higher than FixidityLib.digits() will be truncated accordingly.

**function fromFixed(int256 x, uint8 _destinationDigits)**
**function toFixed(uint256 x, uint8 _originDigits)**
Converts an uint256 which is already in some fixed point representation to that
of this library. The _originDigits parameter is the precision of x. Values with
a precision higher than FixidityLib.digits() will be truncated accordingly.

**function toUint(int256 x)**
Converts an int256 in the fixed point representation of this library to an uint. All decimal digits will be truncated.

**function toInt(int256 x)**
Converts an int256 in the fixed point representation of this library to an uint. All decimal digits will be truncated.

**function toUint(int256 x, uint8 _destinationDigits)**
Converts an int256 in the fixed point representation of this library to a
different representation. The _destinationDigits parameter is the precision of
different fixed point representation stored in an uint256. The _destinationDigits parameter is the precision of
the output x. Values with a precision below than FixidityLib.digits() will be
truncated accordingly.

**function newFixedFraction(int256 numerator, int256 denominator)**
Converts two int256 representing a fraction to fixed point units, equivalent to
multiplying dividend and divisor by 10^digits() and then dividing them.
**function toInt(int256 x, uint8 _destinationDigits)**
Converts an int256 in the fixed point representation of this library to a
different fixed point representation stored in uint256. The _destinationDigits parameter is the precision of
the output x. Values with a precision below than FixidityLib.digits() will be
truncated accordingly.

**function integer(int256 x) public pure returns (int256)**
Returns the integer part of a fixed point number, still in fixed point format.
Expand Down Expand Up @@ -171,6 +196,33 @@ x/y. If the dividend is higher than maxFixedDiv() it might overflow. You can
use multiply(x,reciprocal(y)) instead.
There is a loss of precision on division for the lower mulPrecision() decimals.

** Obsolete functions kept for backwards compatibility**

**function newFixed(int256 x)**
Converts an int256 to fixed point units, equivalent to multiplying by
10^digits().

**function fromFixed(int256 x)**
Converts an int256 in the fixed point representation of this library to a non
decimal. All decimal digits will be truncated.

**function newFixed(int256 x, uint8 _originDigits)**
Converts an int256 which is already in some fixed point representation to that
of this library. The _originDigits parameter is the precision of x. Values with
a precision higher than FixidityLib.digits() will be truncated accordingly.

**function fromFixed(int256 x, uint8 _destinationDigits)**
Converts an int256 in the fixed point representation of this library to a
different representation. The _destinationDigits parameter is the precision of
the output x. Values with a precision below than FixidityLib.digits() will be
truncated accordingly.

**function newFixedFraction(int256 numerator, int256 denominator)**
Converts two int256 representing a fraction to fixed point units, equivalent to
multiplying dividend and divisor by 10^digits() and then dividing them.



## LogarithmLib.sol
This library extends FixidityLib by implementing logarithms, along with the related constants and limits.

Expand Down
40 changes: 8 additions & 32 deletions contracts/ExponentLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@ import "./LogarithmLib.sol";

library ExponentLib {

function fixedExp10() public pure returns(int256) {
function fixedExp10() internal pure returns(int256) {
return 22026465794806716516957900645;
}

/**
* @notice Not fully tested anymore.
*/
function powerE(int256 _x)
public
pure
returns (int256)
{
assert(_x < 172 * FixidityLib.fixed1());
function powerE(int256 _x) internal pure returns (int256) {
require(_x < 172 * FixidityLib.fixed1());
int256 x = _x;
int256 r = FixidityLib.fixed1();
while (x >= 10 * FixidityLib.fixed1()) {
Expand All @@ -39,36 +35,20 @@ library ExponentLib {
return trunc_digits(FixidityLib.multiply(tr, r), 2);
}

function powerAny(int256 a, int256 b)
public
pure
returns (int256)
{
function powerAny(int256 a, int256 b) internal pure returns (int256) {
return powerE(FixidityLib.multiply(LogarithmLib.ln(a), b));
}

function rootAny(int256 a, int256 b)
public
pure
returns (int256)
{
function rootAny(int256 a, int256 b) internal pure returns (int256) {
return powerAny(a, FixidityLib.reciprocal(b));
}

function rootN(int256 a, uint8 n)
public
pure
returns (int256)
{
function rootN(int256 a, uint8 n) internal pure returns (int256) {
return powerE(FixidityLib.divide(LogarithmLib.ln(a), FixidityLib.fixed1() * n));
}

// solium-disable-next-line mixedcase
function round_off(int256 _v, uint8 digits)
public
pure
returns (int256)
{
function round_off(int256 _v, uint8 digits) internal pure returns (int256) {
int256 t = int256(uint256(10) ** uint256(digits));
int8 sign = 1;
int256 v = _v;
Expand All @@ -81,11 +61,7 @@ library ExponentLib {
}

// solium-disable-next-line mixedcase
function trunc_digits(int256 v, uint8 digits)
public
pure
returns (int256)
{
function trunc_digits(int256 v, uint8 digits) internal pure returns (int256) {
if (digits <= 0) return v;
return round_off(v, digits) / FixidityLib.fixed1();
}
Expand Down
Loading