Skip to content

Commit

Permalink
feat: init
Browse files Browse the repository at this point in the history
  • Loading branch information
Akagi201 committed Aug 14, 2024
1 parent 648c6f0 commit 05a02cb
Show file tree
Hide file tree
Showing 13 changed files with 493 additions and 62 deletions.
5 changes: 0 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,3 @@ jobs:
run: |
forge build --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"solidity.packageDefaultDependenciesContractsDirectory": "src",
"solidity.packageDefaultDependenciesDirectory": "lib"
}
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
forge-std/=lib/forge-std/src/
19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

34 changes: 34 additions & 0 deletions src/AddressStringUtil.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.26;

library AddressStringUtil {
// converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2)
function toAsciiString(address addr, uint256 len) internal pure returns (string memory) {
require(len % 2 == 0 && len > 0 && len <= 40, "AddressStringUtil: INVALID_LEN");

bytes memory s = new bytes(len);
uint256 addrNum = uint256(uint160(addr));
for (uint256 i = 0; i < len / 2; i++) {
// shift right and truncate all but the least significant byte to extract the byte at position 19-i
uint8 b = uint8(addrNum >> (8 * (19 - i)));
// first hex character is the most significant 4 bits
uint8 hi = b >> 4;
// second hex character is the least significant 4 bits
uint8 lo = b - (hi << 4);
s[2 * i] = char(hi);
s[2 * i + 1] = char(lo);
}
return string(s);
}

// hi and lo are only 4 bits and between 0 and 16
// this method converts those values to the unicode/ascii code point for the hex representation
// uses upper case for the characters
function char(uint8 b) private pure returns (bytes1 c) {
if (b < 10) {
return bytes1(b + 0x30);
} else {
return bytes1(b + 0x37);
}
}
}
52 changes: 52 additions & 0 deletions src/Babylonian.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.26;

// computes square roots using the babylonian method
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
library Babylonian {
// credit for this implementation goes to
// https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
function sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
// this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
// however that code costs significantly more gas
uint256 xx = x;
uint256 r = 1;
if (xx >= 0x100000000000000000000000000000000) {
xx >>= 128;
r <<= 64;
}
if (xx >= 0x10000000000000000) {
xx >>= 64;
r <<= 32;
}
if (xx >= 0x100000000) {
xx >>= 32;
r <<= 16;
}
if (xx >= 0x10000) {
xx >>= 16;
r <<= 8;
}
if (xx >= 0x100) {
xx >>= 8;
r <<= 4;
}
if (xx >= 0x10) {
xx >>= 4;
r <<= 2;
}
if (xx >= 0x8) {
r <<= 1;
}
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1; // Seven iterations should be enough
uint256 r1 = x / r;
return (r < r1 ? r : r1);
}
}
85 changes: 85 additions & 0 deletions src/BitMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.26;

library BitMath {
// returns the 0 indexed position of the most significant bit of the input x
// s.t. x >= 2**msb and x < 2**(msb+1)
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0, "BitMath::mostSignificantBit: zero");

if (x >= 0x100000000000000000000000000000000) {
x >>= 128;
r += 128;
}
if (x >= 0x10000000000000000) {
x >>= 64;
r += 64;
}
if (x >= 0x100000000) {
x >>= 32;
r += 32;
}
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 0x4) {
x >>= 2;
r += 2;
}
if (x >= 0x2) r += 1;
}

// returns the 0 indexed position of the least significant bit of the input x
// s.t. (x & 2**lsb) != 0 and (x & (2**(lsb) - 1)) == 0)
// i.e. the bit at the index is set and the mask of all lower bits is 0
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0, "BitMath::leastSignificantBit: zero");

r = 255;
if (x & type(uint128).max > 0) {
r -= 128;
} else {
x >>= 128;
}
if (x & type(uint64).max > 0) {
r -= 64;
} else {
x >>= 64;
}
if (x & type(uint32).max > 0) {
r -= 32;
} else {
x >>= 32;
}
if (x & type(uint16).max > 0) {
r -= 16;
} else {
x >>= 16;
}
if (x & type(uint8).max > 0) {
r -= 8;
} else {
x >>= 8;
}
if (x & 0xf > 0) {
r -= 4;
} else {
x >>= 4;
}
if (x & 0x3 > 0) {
r -= 2;
} else {
x >>= 2;
}
if (x & 0x1 > 0) r -= 1;
}
}
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

146 changes: 146 additions & 0 deletions src/FixedPoint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.26;

import "./FullMath.sol";
import "./Babylonian.sol";
import "./BitMath.sol";

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
library FixedPoint {
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
struct uq112x112 {
uint224 _x;
}

// range: [0, 2**144 - 1]
// resolution: 1 / 2**112
struct uq144x112 {
uint256 _x;
}

uint8 public constant RESOLUTION = 112;
uint256 public constant Q112 = 0x10000000000000000000000000000; // 2**112
uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; // 2**224
uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits)

// encode a uint112 as a UQ112x112
function encode(uint112 x) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(x) << RESOLUTION);
}

// encodes a uint144 as a UQ144x112
function encode144(uint144 x) internal pure returns (uq144x112 memory) {
return uq144x112(uint256(x) << RESOLUTION);
}

// decode a UQ112x112 into a uint112 by truncating after the radix point
function decode(uq112x112 memory self) internal pure returns (uint112) {
return uint112(self._x >> RESOLUTION);
}

// decode a UQ144x112 into a uint144 by truncating after the radix point
function decode144(uq144x112 memory self) internal pure returns (uint144) {
return uint144(self._x >> RESOLUTION);
}

// multiply a UQ112x112 by a uint, returning a UQ144x112
// reverts on overflow
function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) {
uint256 z = 0;
require(y == 0 || (z = self._x * y) / y == self._x, "FixedPoint::mul: overflow");
return uq144x112(z);
}

// multiply a UQ112x112 by an int and decode, returning an int
// reverts on overflow
function muli(uq112x112 memory self, int256 y) internal pure returns (int256) {
uint256 z = FullMath.mulDiv(self._x, uint256(y < 0 ? -y : y), Q112);
require(z < 2 ** 255, "FixedPoint::muli: overflow");
return y < 0 ? -int256(z) : int256(z);
}

// multiply a UQ112x112 by a UQ112x112, returning a UQ112x112
// lossy
function muluq(uq112x112 memory self, uq112x112 memory other) internal pure returns (uq112x112 memory) {
if (self._x == 0 || other._x == 0) {
return uq112x112(0);
}
uint112 upper_self = uint112(self._x >> RESOLUTION); // * 2^0
uint112 lower_self = uint112(self._x & LOWER_MASK); // * 2^-112
uint112 upper_other = uint112(other._x >> RESOLUTION); // * 2^0
uint112 lower_other = uint112(other._x & LOWER_MASK); // * 2^-112

// partial products
uint224 upper = uint224(upper_self) * upper_other; // * 2^0
uint224 lower = uint224(lower_self) * lower_other; // * 2^-224
uint224 uppers_lowero = uint224(upper_self) * lower_other; // * 2^-112
uint224 uppero_lowers = uint224(upper_other) * lower_self; // * 2^-112

// so the bit shift does not overflow
require(upper <= type(uint112).max, "FixedPoint::muluq: upper overflow");

// this cannot exceed 256 bits, all values are 224 bits
uint256 sum = uint256(upper << RESOLUTION) + uppers_lowero + uppero_lowers + (lower >> RESOLUTION);

// so the cast does not overflow
require(sum <= type(uint224).max, "FixedPoint::muluq: sum overflow");

return uq112x112(uint224(sum));
}

// divide a UQ112x112 by a UQ112x112, returning a UQ112x112
function divuq(uq112x112 memory self, uq112x112 memory other) internal pure returns (uq112x112 memory) {
require(other._x > 0, "FixedPoint::divuq: division by zero");
if (self._x == other._x) {
return uq112x112(uint224(Q112));
}
if (self._x <= type(uint144).max) {
uint256 value = (uint256(self._x) << RESOLUTION) / other._x;
require(value <= type(uint224).max, "FixedPoint::divuq: overflow");
return uq112x112(uint224(value));
}

uint256 result = FullMath.mulDiv(Q112, self._x, other._x);
require(result <= type(uint224).max, "FixedPoint::divuq: overflow");
return uq112x112(uint224(result));
}

// returns a UQ112x112 which represents the ratio of the numerator to the denominator
// can be lossy
function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) {
require(denominator > 0, "FixedPoint::fraction: division by zero");
if (numerator == 0) return FixedPoint.uq112x112(0);

if (numerator <= type(uint144).max) {
uint256 result = (numerator << RESOLUTION) / denominator;
require(result <= type(uint224).max, "FixedPoint::fraction: overflow");
return uq112x112(uint224(result));
} else {
uint256 result = FullMath.mulDiv(numerator, Q112, denominator);
require(result <= type(uint224).max, "FixedPoint::fraction: overflow");
return uq112x112(uint224(result));
}
}

// take the reciprocal of a UQ112x112
// reverts on overflow
// lossy
function reciprocal(uq112x112 memory self) internal pure returns (uq112x112 memory) {
require(self._x != 0, "FixedPoint::reciprocal: reciprocal of zero");
require(self._x != 1, "FixedPoint::reciprocal: overflow");
return uq112x112(uint224(Q224 / self._x));
}

// square root of a UQ112x112
// lossy between 0/1 and 40 bits
function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) {
if (self._x <= type(uint144).max) {
return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112)));
}

uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x);
safeShiftBits -= safeShiftBits % 2;
return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2)));
}
}
Loading

0 comments on commit 05a02cb

Please sign in to comment.