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

Quoter update #349

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1 +1 @@
159043
144072
Original file line number Diff line number Diff line change
@@ -1 +1 @@
166396
149523
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
93637
78599
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
100303
83407
Original file line number Diff line number Diff line change
@@ -1 +1 @@
141321
120493
Original file line number Diff line number Diff line change
@@ -1 +1 @@
164528
145416
Original file line number Diff line number Diff line change
@@ -1 +1 @@
98641
79439
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_quoteExactInput_twoHops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
234806
201179
Original file line number Diff line number Diff line change
@@ -1 +1 @@
161346
119828
Original file line number Diff line number Diff line change
@@ -1 +1 @@
191453
149965
Original file line number Diff line number Diff line change
@@ -1 +1 @@
161661
119896
Original file line number Diff line number Diff line change
@@ -1 +1 @@
136430
96595
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_quoteExactOutput_twoHops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
275720
200674
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[profile.default]
out = 'foundry-out'
solc_version = '0.8.26'
optimizer_runs = 1_000_000
optimizer_runs = 44444444
via_ir = true
ffi = true
fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}]
Expand Down
62 changes: 62 additions & 0 deletions src/base/BaseV4Quoter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: UNLICENSED

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {QuoterRevert} from "../libraries/QuoterRevert.sol";
import {SqrtPriceLimitHelper} from "../libraries/SqrtPriceLimitHelper.sol";
import {SafeCallback} from "../base/SafeCallback.sol";
import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";

abstract contract BaseV4Quoter is SafeCallback {
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
using SqrtPriceLimitHelper for uint160;
using QuoterRevert for *;
using PoolIdLibrary for PoolId;

error NotEnoughLiquidity(PoolId poolId);
error NotSelf();
error UnexpectedCallSuccess();

constructor(IPoolManager _poolManager) SafeCallback(_poolManager) {}

/// @dev Only this address may call this function. Used to mimic internal functions, using an
/// external call to catch and parse revert reasons
modifier selfOnly() {
if (msg.sender != address(this)) revert NotSelf();
_;
}

function _unlockCallback(bytes calldata data) internal override returns (bytes memory) {
(bool success, bytes memory returnData) = address(this).call(data);
// Every quote path gathers a quote, and then reverts either with QuoteSwap(quoteAmount) or alternative error
if (success) revert UnexpectedCallSuccess();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just starting to look through this but my first thought here.. is I'm wondering if its even possible that this returns true? Like could you write a test that hits this revert?

// Bubble the revert string, whether a valid quote or an alternative error
returnData.bubbleReason();
}

/// @dev Execute a swap and return the balance delta
/// @notice if amountSpecified < 0, the swap is exactInput, otherwise exactOutput
function _swap(
PoolKey memory poolKey,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata hookData
) internal returns (BalanceDelta swapDelta) {
swapDelta = poolManager.swap(
poolKey,
IPoolManager.SwapParams({
zeroForOne: zeroForOne,
amountSpecified: amountSpecified,
sqrtPriceLimitX96: sqrtPriceLimitX96.getSqrtPriceLimit(zeroForOne)
}),
hookData
);

// Check that the pool was not illiquid.
int128 amountSpecifiedActual = (zeroForOne == (amountSpecified < 0)) ? swapDelta.amount0() : swapDelta.amount1();
if (sqrtPriceLimitX96 == 0 && amountSpecifiedActual != amountSpecified) {
revert NotEnoughLiquidity(poolKey.toId());
}
}
}
50 changes: 13 additions & 37 deletions src/interfaces/IQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,10 @@ import {PathKey} from "../libraries/PathKey.sol";

/// @title Quoter Interface
/// @notice Supports quoting the delta amounts for exact input or exact output swaps.
/// @notice For each pool also tells you the number of initialized ticks loaded and the sqrt price of the pool after the swap.
/// @notice For each pool also tells you the sqrt price of the pool after the swap.
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IQuoter {
error InvalidLockCaller();
error InvalidQuoteBatchParams();
error InsufficientAmountOut();
error LockFailure();
error NotSelf();
error UnexpectedRevertBytes(bytes revertData);

struct PoolDeltas {
int128 currency0Delta;
int128 currency1Delta;
}

struct QuoteExactSingleParams {
PoolKey poolKey;
bool zeroForOne;
Expand All @@ -44,28 +32,22 @@ interface IQuoter {
/// exactAmount The desired input amount
/// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// hookData arbitrary hookData to pass into the associated hooks
/// @return deltaAmounts Delta amounts resulted from the swap
/// @return sqrtPriceX96After The sqrt price of the pool after the swap
/// @return initializedTicksLoaded The number of initialized ticks that the swap loaded
/// @return amountOut The output quote for the exactIn swap
/// @return gasEstimate Estimated gas units used for the swap
function quoteExactInputSingle(QuoteExactSingleParams memory params)
external
returns (int128[] memory deltaAmounts, uint160 sqrtPriceX96After, uint32 initializedTicksLoaded);
returns (uint256 amountOut, uint256 gasEstimate);

/// @notice Returns the delta amounts along the swap path for a given exact input swap
/// @param params the params for the quote, encoded as 'QuoteExactParams'
/// currencyIn The input currency of the swap
/// path The path of the swap encoded as PathKeys that contains currency, fee, tickSpacing, and hook info
/// exactAmount The desired input amount
/// @return deltaAmounts Delta amounts along the path resulted from the swap
/// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
/// @return initializedTicksLoadedList List of the initialized ticks that the swap loaded for each pool in the path
/// @return amountOut The output quote for the exactIn swap
/// @return gasEstimate Estimated gas units used for the swap
function quoteExactInput(QuoteExactParams memory params)
external
returns (
int128[] memory deltaAmounts,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksLoadedList
);
returns (uint256 amountOut, uint256 gasEstimate);

/// @notice Returns the delta amounts for a given exact output swap of a single pool
/// @param params The params for the quote, encoded as `QuoteExactSingleParams`
Expand All @@ -74,26 +56,20 @@ interface IQuoter {
/// exactAmount The desired output amount
/// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// hookData arbitrary hookData to pass into the associated hooks
/// @return deltaAmounts Delta amounts resulted from the swap
/// @return sqrtPriceX96After The sqrt price of the pool after the swap
/// @return initializedTicksLoaded The number of initialized ticks that the swap loaded
/// @return amountIn The input quote for the exactOut swap
/// @return gasEstimate Estimated gas units used for the swap
function quoteExactOutputSingle(QuoteExactSingleParams memory params)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to add batched functions so you can get many quotes in one call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they use an external multicall contract for multicalling quoters onchain
on mainnet its here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah cool

external
returns (int128[] memory deltaAmounts, uint160 sqrtPriceX96After, uint32 initializedTicksLoaded);
returns (uint256 amountIn, uint256 gasEstimate);

/// @notice Returns the delta amounts along the swap path for a given exact output swap
/// @param params the params for the quote, encoded as 'QuoteExactParams'
/// currencyOut The output currency of the swap
/// path The path of the swap encoded as PathKeys that contains currency, fee, tickSpacing, and hook info
/// exactAmount The desired output amount
/// @return deltaAmounts Delta amounts along the path resulted from the swap
/// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
/// @return initializedTicksLoadedList List of the initialized ticks that the swap loaded for each pool in the path
/// @return amountIn The input quote for the exactOut swap
/// @return gasEstimate Estimated gas units used for the swap
function quoteExactOutput(QuoteExactParams memory params)
external
returns (
int128[] memory deltaAmounts,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksLoadedList
);
returns (uint256 amountIn, uint256 gasEstimate);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I think we should actually return the BalanceDelta, will follow up async w you

}
Loading
Loading