-
Notifications
You must be signed in to change notification settings - Fork 220
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
super optimised ETH for USDC swap (#325)
* super optimised ETH for USDC swap * updated v3 math versions * add ETH wrap --------- Co-authored-by: hensha256 <[email protected]>
- Loading branch information
1 parent
6c8b844
commit c6614f7
Showing
7 changed files
with
572 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// pragma solidity >=0.5.0 <0.8.0 | ||
pragma solidity ^0.8.17; | ||
|
||
|
||
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; | ||
import './updated/updatedTickMath.sol'; | ||
import './updated/updatedFullMath.sol'; | ||
|
||
// import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; | ||
// import {V2SwapRouter} from './modules/uniswap/v2/V2SwapRouter.sol'; | ||
|
||
/// @title Oracle library | ||
/// @notice Provides functions to integrate with V3 pool oracle | ||
library OracleLibrary { | ||
/// @notice Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool | ||
/// @param pool Address of the pool that we want to observe | ||
/// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means | ||
/// @return arithmeticMeanTick The arithmetic mean tick from (block.timestamp - secondsAgo) to block.timestamp | ||
/// @return harmonicMeanLiquidity The harmonic mean liquidity from (block.timestamp - secondsAgo) to block.timestamp | ||
function consult(address pool, uint32 secondsAgo) | ||
internal | ||
view | ||
returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity) | ||
{ | ||
require(secondsAgo != 0, 'BP'); | ||
|
||
uint32[] memory secondsAgos = new uint32[](2); | ||
secondsAgos[0] = secondsAgo; | ||
secondsAgos[1] = 0; | ||
|
||
(int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = | ||
IUniswapV3Pool(pool).observe(secondsAgos); | ||
|
||
int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; | ||
uint160 secondsPerLiquidityCumulativesDelta = | ||
secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0]; | ||
|
||
arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(secondsAgo))); | ||
// Always round to negative infinity | ||
if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int56(uint56(secondsAgo)) != 0)) arithmeticMeanTick--; | ||
|
||
// We are multiplying here instead of shifting to ensure that harmonicMeanLiquidity doesn't overflow uint128 | ||
uint192 secondsAgoX160 = uint192(secondsAgo) * type(uint160).max; | ||
harmonicMeanLiquidity = uint128(secondsAgoX160 / (uint192(secondsPerLiquidityCumulativesDelta) << 32)); | ||
} | ||
|
||
/// @notice Given a tick and a token amount, calculates the amount of token received in exchange | ||
/// @param tick Tick value used to calculate the quote | ||
/// @param baseAmount Amount of token to be converted | ||
/// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination | ||
/// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination | ||
/// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken | ||
function getQuoteAtTick( | ||
int24 tick, | ||
uint128 baseAmount, | ||
address baseToken, | ||
address quoteToken | ||
) internal pure returns (uint256 quoteAmount) { | ||
uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); | ||
|
||
// Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself | ||
if (sqrtRatioX96 <= type(uint128).max) { | ||
uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; | ||
quoteAmount = baseToken < quoteToken | ||
? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) | ||
: FullMath.mulDiv(1 << 192, baseAmount, ratioX192); | ||
} else { | ||
uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); | ||
quoteAmount = baseToken < quoteToken | ||
? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) | ||
: FullMath.mulDiv(1 << 128, baseAmount, ratioX128); | ||
} | ||
} | ||
|
||
/// @notice Given a pool, it returns the number of seconds ago of the oldest stored observation | ||
/// @param pool Address of Uniswap V3 pool that we want to observe | ||
/// @return secondsAgo The number of seconds ago of the oldest observation stored for the pool | ||
function getOldestObservationSecondsAgo(address pool) internal view returns (uint32 secondsAgo) { | ||
(, , uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); | ||
require(observationCardinality > 0, 'NI'); | ||
|
||
(uint32 observationTimestamp, , , bool initialized) = | ||
IUniswapV3Pool(pool).observations((observationIndex + 1) % observationCardinality); | ||
|
||
// The next index might not be initialized if the cardinality is in the process of increasing | ||
// In this case the oldest observation is always in index 0 | ||
if (!initialized) { | ||
(observationTimestamp, , , ) = IUniswapV3Pool(pool).observations(0); | ||
} | ||
|
||
secondsAgo = uint32(block.timestamp) - observationTimestamp; | ||
} | ||
|
||
/// @notice Given a pool, it returns the tick value as of the start of the current block | ||
/// @param pool Address of Uniswap V3 pool | ||
/// @return The tick that the pool was in at the start of the current block | ||
function getBlockStartingTickAndLiquidity(address pool) internal view returns (int24, uint128) { | ||
(, int24 tick, uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); | ||
|
||
// 2 observations are needed to reliably calculate the block starting tick | ||
require(observationCardinality > 1, 'NEO'); | ||
|
||
// If the latest observation occurred in the past, then no tick-changing trades have happened in this block | ||
// therefore the tick in `slot0` is the same as at the beginning of the current block. | ||
// We don't need to check if this observation is initialized - it is guaranteed to be. | ||
(uint32 observationTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, ) = | ||
IUniswapV3Pool(pool).observations(observationIndex); | ||
if (observationTimestamp != uint32(block.timestamp)) { | ||
return (tick, IUniswapV3Pool(pool).liquidity()); | ||
} | ||
|
||
uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality; | ||
( | ||
uint32 prevObservationTimestamp, | ||
int56 prevTickCumulative, | ||
uint160 prevSecondsPerLiquidityCumulativeX128, | ||
bool prevInitialized | ||
) = IUniswapV3Pool(pool).observations(prevIndex); | ||
|
||
require(prevInitialized, 'ONI'); | ||
|
||
uint32 delta = observationTimestamp - prevObservationTimestamp; | ||
|
||
tick = int24((tickCumulative - prevTickCumulative) / int56(uint56(delta))); | ||
uint128 liquidity = | ||
uint128( | ||
(uint192(delta) * type(uint160).max) / | ||
(uint192(secondsPerLiquidityCumulativeX128 - prevSecondsPerLiquidityCumulativeX128) << 32) | ||
); | ||
return (tick, liquidity); | ||
} | ||
|
||
/// @notice Information for calculating a weighted arithmetic mean tick | ||
struct WeightedTickData { | ||
int24 tick; | ||
uint128 weight; | ||
} | ||
|
||
/// @notice Given an array of ticks and weights, calculates the weighted arithmetic mean tick | ||
/// @param weightedTickData An array of ticks and weights | ||
/// @return weightedArithmeticMeanTick The weighted arithmetic mean tick | ||
/// @dev Each entry of `weightedTickData` should represents ticks from pools with the same underlying pool tokens. If they do not, | ||
/// extreme care must be taken to ensure that ticks are comparable (including decimal differences). | ||
/// @dev Note that the weighted arithmetic mean tick corresponds to the weighted geometric mean price. | ||
function getWeightedArithmeticMeanTick(WeightedTickData[] memory weightedTickData) | ||
internal | ||
pure | ||
returns (int24 weightedArithmeticMeanTick) | ||
{ | ||
// Accumulates the sum of products between each tick and its weight | ||
int256 numerator; | ||
|
||
// Accumulates the sum of the weights | ||
uint256 denominator; | ||
|
||
// Products fit in 152 bits, so it would take an array of length ~2**104 to overflow this logic | ||
for (uint256 i; i < weightedTickData.length; i++) { | ||
numerator += weightedTickData[i].tick * int256(uint256(weightedTickData[i].weight)); | ||
denominator += weightedTickData[i].weight; | ||
} | ||
|
||
weightedArithmeticMeanTick = int24(numerator / int256(denominator)); | ||
// Always round to negative infinity | ||
if (numerator < 0 && (numerator % int256(denominator) != 0)) weightedArithmeticMeanTick--; | ||
} | ||
|
||
/// @notice Returns the "synthetic" tick which represents the price of the first entry in `tokens` in terms of the last | ||
/// @dev Useful for calculating relative prices along routes. | ||
/// @dev There must be one tick for each pairwise set of tokens. | ||
/// @param tokens The token contract addresses | ||
/// @param ticks The ticks, representing the price of each token pair in `tokens` | ||
/// @return syntheticTick The synthetic tick, representing the relative price of the outermost tokens in `tokens` | ||
function getChainedPrice(address[] memory tokens, int24[] memory ticks) | ||
internal | ||
pure | ||
returns (int256 syntheticTick) | ||
{ | ||
require(tokens.length - 1 == ticks.length, 'DL'); | ||
for (uint256 i = 1; i <= ticks.length; i++) { | ||
// check the tokens for address sort order, then accumulate the | ||
// ticks into the running synthetic tick, ensuring that intermediate tokens "cancel out" | ||
tokens[i - 1] < tokens[i] ? syntheticTick += ticks[i - 1] : syntheticTick -= ticks[i - 1]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
contracts/modules/uniswap/v3/updated/updatedFullMath.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
/// @title Contains 512-bit math functions | ||
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision | ||
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits | ||
library FullMath { | ||
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 | ||
/// @param a The multiplicand | ||
/// @param b The multiplier | ||
/// @param denominator The divisor | ||
/// @return result The 256-bit result | ||
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv | ||
function mulDiv( | ||
uint256 a, | ||
uint256 b, | ||
uint256 denominator | ||
) internal pure returns (uint256 result) { | ||
unchecked { | ||
// 512-bit multiply [prod1 prod0] = a * b | ||
// Compute the product mod 2**256 and mod 2**256 - 1 | ||
// then use the Chinese Remainder Theorem to reconstruct | ||
// the 512 bit result. The result is stored in two 256 | ||
// variables such that product = prod1 * 2**256 + prod0 | ||
uint256 prod0; // Least significant 256 bits of the product | ||
uint256 prod1; // Most significant 256 bits of the product | ||
assembly { | ||
let mm := mulmod(a, b, not(0)) | ||
prod0 := mul(a, b) | ||
prod1 := sub(sub(mm, prod0), lt(mm, prod0)) | ||
} | ||
|
||
// Handle non-overflow cases, 256 by 256 division | ||
if (prod1 == 0) { | ||
require(denominator > 0); | ||
assembly { | ||
result := div(prod0, denominator) | ||
} | ||
return result; | ||
} | ||
|
||
// Make sure the result is less than 2**256. | ||
// Also prevents denominator == 0 | ||
require(denominator > prod1); | ||
|
||
/////////////////////////////////////////////// | ||
// 512 by 256 division. | ||
/////////////////////////////////////////////// | ||
|
||
// Make division exact by subtracting the remainder from [prod1 prod0] | ||
// Compute remainder using mulmod | ||
uint256 remainder; | ||
assembly { | ||
remainder := mulmod(a, b, denominator) | ||
} | ||
// Subtract 256 bit number from 512 bit number | ||
assembly { | ||
prod1 := sub(prod1, gt(remainder, prod0)) | ||
prod0 := sub(prod0, remainder) | ||
} | ||
|
||
// Factor powers of two out of denominator | ||
// Compute largest power of two divisor of denominator. | ||
// Always >= 1. | ||
uint256 twos = (0 - denominator) & denominator; | ||
// Divide denominator by power of two | ||
assembly { | ||
denominator := div(denominator, twos) | ||
} | ||
|
||
// Divide [prod1 prod0] by the factors of two | ||
assembly { | ||
prod0 := div(prod0, twos) | ||
} | ||
// Shift in bits from prod1 into prod0. For this we need | ||
// to flip `twos` such that it is 2**256 / twos. | ||
// If twos is zero, then it becomes one | ||
assembly { | ||
twos := add(div(sub(0, twos), twos), 1) | ||
} | ||
prod0 |= prod1 * twos; | ||
|
||
// Invert denominator mod 2**256 | ||
// Now that denominator is an odd number, it has an inverse | ||
// modulo 2**256 such that denominator * inv = 1 mod 2**256. | ||
// Compute the inverse by starting with a seed that is correct | ||
// correct for four bits. That is, denominator * inv = 1 mod 2**4 | ||
uint256 inv = (3 * denominator) ^ 2; | ||
// Now use Newton-Raphson iteration to improve the precision. | ||
// Thanks to Hensel's lifting lemma, this also works in modular | ||
// arithmetic, doubling the correct bits in each step. | ||
inv *= 2 - denominator * inv; // inverse mod 2**8 | ||
inv *= 2 - denominator * inv; // inverse mod 2**16 | ||
inv *= 2 - denominator * inv; // inverse mod 2**32 | ||
inv *= 2 - denominator * inv; // inverse mod 2**64 | ||
inv *= 2 - denominator * inv; // inverse mod 2**128 | ||
inv *= 2 - denominator * inv; // inverse mod 2**256 | ||
|
||
// Because the division is now exact we can divide by multiplying | ||
// with the modular inverse of denominator. This will give us the | ||
// correct result modulo 2**256. Since the precoditions guarantee | ||
// that the outcome is less than 2**256, this is the final result. | ||
// We don't need to compute the high bits of the result and prod1 | ||
// is no longer required. | ||
result = prod0 * inv; | ||
return result; | ||
} | ||
} | ||
|
||
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 | ||
/// @param a The multiplicand | ||
/// @param b The multiplier | ||
/// @param denominator The divisor | ||
/// @return result The 256-bit result | ||
function mulDivRoundingUp( | ||
uint256 a, | ||
uint256 b, | ||
uint256 denominator | ||
) internal pure returns (uint256 result) { | ||
unchecked { | ||
result = mulDiv(a, b, denominator); | ||
if (mulmod(a, b, denominator) > 0) { | ||
require(result < type(uint256).max); | ||
result++; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.