Skip to content

Instantly share code, notes, and snippets.

@sunnyRK
Created December 28, 2021 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sunnyRK/465f05c9a5f97d8b9c377968ce3296c4 to your computer and use it in GitHub Desktop.
Save sunnyRK/465f05c9a5f97d8b9c377968ce3296c4 to your computer and use it in GitHub Desktop.
// 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