Created
December 28, 2021 11:44
-
-
Save sunnyRK/465f05c9a5f97d8b9c377968ce3296c4 to your computer and use it in GitHub Desktop.
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
// SPDX-License-Identifier: GPL-2.0-or-later | |
pragma solidity =0.7.6; | |
pragma abicoder v2; | |
import '@uniswap/v3-core/contracts/libraries/SafeCast.sol'; | |
import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; | |
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; | |
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol'; | |
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol'; | |
import '../interfaces/IQuoter.sol'; | |
import '../base/PeripheryImmutableState.sol'; | |
import '../libraries/Path.sol'; | |
import '../libraries/PoolAddress.sol'; | |
import '../libraries/CallbackValidation.sol'; | |
import "hardhat/console.sol"; | |
/// @title Provides quotes for swaps | |
/// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap | |
/// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute | |
/// the swap and check the amounts in the callback. | |
contract Quoter is IQuoter, IUniswapV3SwapCallback, PeripheryImmutableState { | |
using Path for bytes; | |
using SafeCast for uint256; | |
/// @dev Transient storage variable used to check a safety condition in exact output swaps. | |
uint256 private amountOutCached; | |
constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} | |
function getPool( | |
address tokenA, | |
address tokenB, | |
uint24 fee | |
) private view returns (IUniswapV3Pool) { | |
return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee))); | |
} | |
/// @inheritdoc IUniswapV3SwapCallback | |
function uniswapV3SwapCallback( | |
int256 amount0Delta, | |
int256 amount1Delta, | |
bytes memory path | |
) external view override { | |
console.log("[uniswapV3SwapCallback] In Quoter uniswapV3SwapCallback appunto"); | |
console.log("amount0Delta = %s %d", (amount0Delta<0) ? "-":"", (amount0Delta<0) ? uint256(-amount0Delta) : uint256(amount0Delta)); | |
console.log("amount1Delta = %s %d", (amount1Delta<0) ? "-":"", (amount1Delta<0) ? uint256(-amount1Delta) : uint256(amount1Delta)); | |
require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported | |
console.log("[uniswapV3SwapCallback] Require Passed"); | |
(address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); | |
console.log("[uniswapV3SwapCallback] T1"); | |
console.log("tokenIn = ", tokenIn); | |
console.log("tokenOut = ", tokenOut); | |
console.log("fee = ", fee); | |
//CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); | |
console.log("[uniswapV3SwapCallback] T3"); | |
(bool isExactInput, uint256 amountToPay, uint256 amountReceived) = | |
amount0Delta > 0 | |
? (tokenIn < tokenOut, uint256(amount0Delta), uint256(-amount1Delta)) | |
: (tokenOut < tokenIn, uint256(amount1Delta), uint256(-amount0Delta)); | |
if (isExactInput) { | |
console.log("In Quoter uniswapV3SwapCallback exactInput=True"); | |
assembly { | |
let ptr := mload(0x40) | |
mstore(ptr, amountReceived) | |
revert(ptr, 32) | |
} | |
} else { | |
// if the cache has been populated, ensure that the full output amount has been received | |
console.log("In Quoter uniswapV3SwapCallback exactInput=False"); | |
if (amountOutCached != 0) require(amountReceived == amountOutCached); | |
assembly { | |
let ptr := mload(0x40) | |
mstore(ptr, amountToPay) | |
revert(ptr, 32) | |
} | |
} | |
} | |
/// @dev Parses a revert reason that should contain the numeric quote | |
function parseRevertReason(bytes memory reason) private pure returns (uint256) { | |
if (reason.length != 32) { | |
if (reason.length < 68) revert('Unexpected error'); | |
assembly { | |
reason := add(reason, 0x04) | |
} | |
revert(abi.decode(reason, (string))); | |
} | |
return abi.decode(reason, (uint256)); | |
} | |
/// @inheritdoc IQuoter | |
function quoteExactInputSingle( | |
address tokenIn, | |
address tokenOut, | |
uint24 fee, | |
uint256 amountIn, | |
uint160 sqrtPriceLimitX96 | |
) public override returns (uint256 amountOut) { | |
bool zeroForOne = tokenIn < tokenOut; | |
console.log("[quoteExactInputSingle] Start"); | |
IUniswapV3Pool pool = getPool(tokenIn, tokenOut, fee); | |
IUniswapV3Pool t_pool = IUniswapV3Pool(IUniswapV3Factory(factory).getPool(tokenIn, tokenOut, fee)); | |
console.log("Token0 = ", address(tokenIn)); | |
console.log("Token1 = ", address(tokenOut)); | |
console.log("fee = ", fee); | |
console.log("Pool = ", address(pool)); | |
console.log("T_Pool = ", address(t_pool)); | |
try | |
t_pool.swap( | |
address(this), // address(0) might cause issues with some tokens | |
zeroForOne, | |
amountIn.toInt256(), | |
sqrtPriceLimitX96 == 0 | |
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) | |
: sqrtPriceLimitX96, | |
abi.encodePacked(tokenIn, fee, tokenOut) | |
) | |
{} | |
catch (bytes memory reason) { | |
string memory temp = string(reason); | |
console.log("[quoteExactInputSingle] Swap Return --> Reason = ", temp); | |
uint256 amountOut = parseRevertReason(reason); | |
console.log("[quoteExactInputSingle] AmountOut = ", amountOut); | |
return amountOut; | |
} | |
} | |
/// @inheritdoc IQuoter | |
function quoteExactInput(bytes memory path, uint256 amountIn) external override returns (uint256 amountOut) { | |
while (true) { | |
bool hasMultiplePools = path.hasMultiplePools(); | |
(address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); | |
// the outputs of prior swaps become the inputs to subsequent ones | |
amountIn = quoteExactInputSingle(tokenIn, tokenOut, fee, amountIn, 0); | |
// decide whether to continue or terminate | |
if (hasMultiplePools) { | |
path = path.skipToken(); | |
} else { | |
return amountIn; | |
} | |
} | |
} | |
/// @inheritdoc IQuoter | |
function quoteExactOutputSingle( | |
address tokenIn, | |
address tokenOut, | |
uint24 fee, | |
uint256 amountOut, | |
uint160 sqrtPriceLimitX96 | |
) public override returns (uint256 amountIn) { | |
bool zeroForOne = tokenIn < tokenOut; | |
// if no price limit has been specified, cache the output amount for comparison in the swap callback | |
if (sqrtPriceLimitX96 == 0) amountOutCached = amountOut; | |
// IUniswapV3Pool pool = getPool(tokenIn, tokenOut, fee); | |
// IUniswapV3Pool t_pool = IUniswapV3Pool(IUniswapV3Factory(factory).getPool(tokenIn, tokenOut, fee)); | |
try | |
IUniswapV3Pool(IUniswapV3Factory(factory).getPool(tokenIn, tokenOut, fee)).swap( | |
address(this), // address(0) might cause issues with some tokens | |
zeroForOne, | |
-amountOut.toInt256(), | |
sqrtPriceLimitX96 == 0 | |
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) | |
: sqrtPriceLimitX96, | |
abi.encodePacked(tokenOut, fee, tokenIn) | |
) | |
{} catch (bytes memory reason) { | |
if (sqrtPriceLimitX96 == 0) delete amountOutCached; // clear cache | |
return parseRevertReason(reason); | |
} | |
} | |
/// @inheritdoc IQuoter | |
function quoteExactOutput(bytes memory path, uint256 amountOut) external override returns (uint256 amountIn) { | |
while (true) { | |
bool hasMultiplePools = path.hasMultiplePools(); | |
(address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool(); | |
// the inputs of prior swaps become the outputs of subsequent ones | |
amountOut = quoteExactOutputSingle(tokenIn, tokenOut, fee, amountOut, 0); | |
// decide whether to continue or terminate | |
if (hasMultiplePools) { | |
path = path.skipToken(); | |
} else { | |
return amountOut; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment