Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate new optimizations #16

Merged
merged 13 commits into from
Jun 28, 2023
14 changes: 7 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BitMathTest:testFuzz_LSB(uint256) (runs: 65536, μ: 14926, ~: 14924)
BitMathTest:testFuzz_LSB(uint256) (runs: 65536, μ: 14923, ~: 14921)
BitMathTest:testFuzz_MSB(uint256) (runs: 65536, μ: 14798, ~: 14822)
BitMathTest:testGas_LSB() (gas: 211488)
BitMathTest:testGas_LSB() (gas: 210723)
BitMathTest:testGas_LSB_Og() (gas: 303324)
BitMathTest:testGas_MSB() (gas: 229272)
BitMathTest:testGas_MSB_Og() (gas: 286673)
Expand Down Expand Up @@ -89,14 +89,14 @@ SwapMathTest:testGas_ComputeSwapStepExactOut() (gas: 380584)
SwapMathTest:testGas_ComputeSwapStepExactOut_Og() (gas: 515145)
SwapMathTest:testGas_ComputeSwapStep_Og() (gas: 531437)
TickBitmapTest:testFuzz_Compress(int24,int24) (runs: 65536, μ: 8410, ~: 8470)
TickBitmapTest:testFuzz_FlipTick(int24) (runs: 65536, μ: 61620, ~: 61625)
TickBitmapTest:testFuzz_NextInitializedTickWithinOneWord(int24,uint8,bool) (runs: 65536, μ: 68053, ~: 68127)
TickBitmapTest:testFuzz_FlipTick(int24) (runs: 65536, μ: 61621, ~: 61625)
TickBitmapTest:testFuzz_NextInitializedTickWithinOneWord(int24,uint8,bool) (runs: 65536, μ: 68049, ~: 68124)
TickBitmapTest:testFuzz_Position(int24) (runs: 65536, μ: 492, ~: 492)
TickBitmapTest:testGas_NextInitializedTickWithinOneWord() (gas: 6798523)
TickBitmapTest:testGas_NextInitializedTickWithinOneWord() (gas: 6795963)
TickBitmapTest:testGas_NextInitializedTickWithinOneWord_Og() (gas: 7062020)
TickBitmapTest:test_NextInitializedTickWithinOneWord_GT() (gas: 204689)
TickBitmapTest:test_NextInitializedTickWithinOneWord_GT() (gas: 203665)
TickBitmapTest:test_NextInitializedTickWithinOneWord_LTE() (gas: 216895)
TickBitmapTest:test_NextInitializedTick_GT() (gas: 359219)
TickBitmapTest:test_NextInitializedTick_GT() (gas: 357171)
TickBitmapTest:test_NextInitializedTick_LTE() (gas: 409484)
TickMathTest:testFuzz_GetSqrtRatioAtTick(int24) (runs: 65536, μ: 16567, ~: 16847)
TickMathTest:testFuzz_GetTickAtSqrtRatio(uint160) (runs: 65536, μ: 13177, ~: 13193)
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
The `uni-v3-lib` by Aperture Finance consists of a suite of Solidity libraries that have been imported and rewritten
from Uniswap's [v3-core](https://github.com/Uniswap/v3-core) and [v3-periphery](https://github.com/Uniswap/v3-periphery)
repositories. This project aims to equip external integrators with a set of libraries crucial for interaction with the
Uniswap V3 protocol. The `uni-v3-lib` is currently in use by
Aperture's [liquidity position automation contract](https://github.com/Aperture-Finance/core-contracts), the audit
report of which can be
found [here](https://github.com/NaryaAI/publications/blob/1468e568712d5e2aa9b0ecde0a16d3f9f1d715ef/Aperture%20UniV3Automan%20Report.pdf).
Uniswap V3 protocol.

## Overview

Expand Down
38 changes: 34 additions & 4 deletions src/TernaryLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,44 @@ library TernaryLib {
}
}

/// @notice Equivalent to: `uint256(x < 0 ? -x : x)`
function abs(int256 x) internal pure returns (uint256 y) {
assembly {
// mask = 0 if x >= 0 else -1
let mask := sub(0, slt(x, 0))
// If x >= 0, |x| = x = 0 ^ x
// If x < 0, |x| = ~~|x| = ~(-|x| - 1) = ~(x - 1) = -1 ^ (x - 1)
// Either case, |x| = mask ^ (x + mask)
y := xor(mask, add(mask, x))
}
}

/// @notice Equivalent to: `a < b ? a : b`
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
function min(uint256 a, uint256 b) internal pure returns (uint256 res) {
assembly {
res := xor(b, mul(xor(a, b), lt(a, b)))
}
}

/// @notice Equivalent to: `a > b ? a : b`
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
function max(uint256 a, uint256 b) internal pure returns (uint256 res) {
assembly {
res := xor(b, mul(xor(a, b), gt(a, b)))
}
}

/// @notice Equivalent to: `condition ? (b, a) : (a, b)`
function switchIf(
bool condition,
uint256 a,
uint256 b
) internal pure returns (uint256, uint256) {
assembly {
let diff := mul(xor(a, b), condition)
a := xor(a, diff)
b := xor(b, diff)
}
return (a, b);
gaohan137 marked this conversation as resolved.
Show resolved Hide resolved
}

/// @notice Equivalent to: `condition ? (b, a) : (a, b)`
Expand Down
6 changes: 3 additions & 3 deletions src/TickBitmap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ library TickBitmap {
}
}

/// @notice Computes the position in the mapping where the initialized bit for a tick lives
/// @notice Computes the word position and the bit position given a tick.
/// @param tick The tick for which to compute the position
/// @return wordPos The key in the mapping containing the word in which the bit is stored
/// @return bitPos The bit position in the word where the flag is stored
Expand Down Expand Up @@ -91,9 +91,9 @@ library TickBitmap {
// (bitPos + 1) may be 256 but fine
// masked = self[wordPos] & mask
assembly ("memory-safe") {
let mask := sub(shl(add(bitPos, 1), 1), 1)
mstore(0, wordPos)
mstore(0x20, self.slot)
let mask := sub(shl(add(bitPos, 1), 1), 1)
masked := and(sload(keccak256(0, 0x40)), mask)
initialized := gt(masked, 0)
}
Expand All @@ -120,9 +120,9 @@ library TickBitmap {
// mask = ~((1 << bitPos) - 1)
// masked = self[wordPos] & mask
assembly ("memory-safe") {
let mask := not(sub(shl(bitPos, 1), 1))
mstore(0, wordPos)
mstore(0x20, self.slot)
let mask := not(sub(shl(bitPos, 1), 1))
masked := and(sload(keccak256(0, 0x40)), mask)
initialized := gt(masked, 0)
}
Expand Down
20 changes: 9 additions & 11 deletions src/TickMath.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import "./TernaryLib.sol";

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-core/blob/main/contracts/libraries/TickMath.sol)
Expand Down Expand Up @@ -30,18 +32,14 @@ library TickMath {
int24 tick
) internal pure returns (uint160 sqrtPriceX96) {
unchecked {
uint256 absTick;
/// @solidity memory-safe-assembly
int256 tick256;
assembly {
// Credit to Solady (https://github.com/Vectorized/solady/blob/1873046d2ed3428e236d83682f302afde369b2c2/src/utils/FixedPointMathLib.sol#L620)
// sign extend to make tick an int256 in twos complement
tick := signextend(2, tick)
// mask = 0 if tick >= 0 else -1
let mask := sub(0, slt(tick, 0))
// If tick >= 0, |tick| = tick = 0 ^ tick
// If tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = -1 ^ (tick - 1)
// Either case, |tick| = mask ^ (tick + mask)
absTick := xor(mask, add(mask, tick))
tick256 := signextend(2, tick)
}
uint256 absTick = TernaryLib.abs(tick256);
/// @solidity memory-safe-assembly
assembly {
// Equivalent: if (absTick > MAX_TICK) revert("T");
if gt(absTick, MAX_TICK) {
// selector "Error(string)", [0x1c, 0x20)
Expand Down Expand Up @@ -114,7 +112,7 @@ library TickMath {

// if (tick > 0) ratio = type(uint256).max / ratio;
assembly {
if sgt(tick, 0) {
if sgt(tick256, 0) {
ratio := div(not(0), ratio)
}
}
Expand Down