Skip to content

Commit

Permalink
move cache into transient storage
Browse files Browse the repository at this point in the history
  • Loading branch information
hensha256 committed Sep 12, 2024
1 parent 356d0e8 commit b1effee
Show file tree
Hide file tree
Showing 16 changed files with 53 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
146153
144148
Original file line number Diff line number Diff line change
@@ -1 +1 @@
151617
149612
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
80841
78941
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
85635
83735
Original file line number Diff line number Diff line change
@@ -1 +1 @@
122679
120674
Original file line number Diff line number Diff line change
@@ -1 +1 @@
147588
145583
Original file line number Diff line number Diff line change
@@ -1 +1 @@
81611
79606
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_quoteExactInput_twoHops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
203446
201436
Original file line number Diff line number Diff line change
@@ -1 +1 @@
142383
120261
Original file line number Diff line number Diff line change
@@ -1 +1 @@
172520
150398
Original file line number Diff line number Diff line change
@@ -1 +1 @@
142451
120329
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119150
97028
2 changes: 1 addition & 1 deletion .forge-snapshots/Quoter_quoteExactOutput_twoHops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
243720
201476
17 changes: 14 additions & 3 deletions src/base/BaseV4Quoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 {CacheAmountSpecified} from "../libraries/CacheAmountSpecified.sol";

abstract contract BaseV4Quoter is SafeCallback {
using SqrtPriceLimitHelper for uint160;
Expand All @@ -17,16 +18,25 @@ abstract contract BaseV4Quoter is SafeCallback {

constructor(IPoolManager _poolManager) SafeCallback(_poolManager) {}

/// @dev cache used to check a safety condition in exact output swaps.
uint128 internal amountOutCached;

/// @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 _clearAmountSpecified() internal {
CacheAmountSpecified.set(0);
}

function _setAmountSpecified(uint256 amountSpecified) internal {
CacheAmountSpecified.set(amountSpecified);
}

function _getAmountSpecified() internal returns (uint256 amountSpecified) {
return CacheAmountSpecified.get();
}

function _unlockCallback(bytes calldata data) internal override returns (bytes memory) {
// Call this contract with the data in question. Each quote path
(bool success, bytes memory returnData) = address(this).call(data);
Expand Down Expand Up @@ -54,6 +64,7 @@ abstract contract BaseV4Quoter is SafeCallback {
hookData
);
// only exactOut case
uint256 amountOutCached = _getAmountSpecified();
if (amountOutCached != 0 && amountOutCached != uint128(zeroForOne ? swapDelta.amount1() : swapDelta.amount0()))
{
revert InsufficientAmountOut();
Expand Down
10 changes: 5 additions & 5 deletions src/lens/Quoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ contract Quoter is IQuoter, BaseV4Quoter {
try poolManager.unlock(abi.encodeCall(this._quoteExactOutputSingle, (params))) {}
catch (bytes memory reason) {
gasEstimate = gasBefore - gasleft();
if (params.sqrtPriceLimitX96 == 0) delete amountOutCached;
if (params.sqrtPriceLimitX96 == 0) _clearAmountSpecified();
amountIn = reason.parseReturnData();
}
}
Expand Down Expand Up @@ -118,14 +118,14 @@ contract Quoter is IQuoter, BaseV4Quoter {

for (uint256 i = pathLength; i > 0; i--) {
pathKey = params.path[i - 1];
amountOutCached = amountOut;
_setAmountSpecified(amountOut);

(PoolKey memory poolKey, bool oneForZero) = pathKey.getPoolAndSwapDirection(outputCurrency);

swapDelta = _swap(poolKey, !oneForZero, int256(uint256(amountOut)), 0, pathKey.hookData);

// always clear because sqrtPriceLimitX96 is set to 0 always
delete amountOutCached;
_clearAmountSpecified();

amountOut = oneForZero ? uint128(-swapDelta.amount1()) : uint128(-swapDelta.amount0());

Expand All @@ -138,7 +138,7 @@ contract Quoter is IQuoter, BaseV4Quoter {
/// @dev external function called within the _unlockCallback, to simulate a single-hop exact output swap, then revert with the result
function _quoteExactOutputSingle(QuoteExactSingleParams calldata params) external selfOnly returns (bytes memory) {
// if no price limit has been specified, cache the output amount for comparison inside the _swap function
if (params.sqrtPriceLimitX96 == 0) amountOutCached = params.exactAmount;
if (params.sqrtPriceLimitX96 == 0) _setAmountSpecified(params.exactAmount);

BalanceDelta swapDelta = _swap(
params.poolKey,
Expand All @@ -148,7 +148,7 @@ contract Quoter is IQuoter, BaseV4Quoter {
params.hookData
);

if (amountOutCached != 0) delete amountOutCached;
_clearAmountSpecified();

// the input delta of a swap is negative so we must flip it
uint256 amountIn = params.zeroForOne ? uint128(-swapDelta.amount0()) : uint128(-swapDelta.amount1());
Expand Down
21 changes: 21 additions & 0 deletions src/libraries/CacheAmountSpecified.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

/// @notice This is a temporary library that allows us to use transient storage (tstore/tload)
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library CacheAmountSpecified {
// bytes32(uint256(keccak256("AmountSpecified")) - 1)
bytes32 internal constant AMOUNT_SPECIFIED_SLOT = 0x040affe16a79096ebfed488ed052568637be7285d797c191b3a85e60cf8292f3;

function set(uint256 amountSpecified) internal {
assembly ("memory-safe") {
tstore(AMOUNT_SPECIFIED_SLOT, amountSpecified)
}
}

function get() internal view returns (uint256 amountSpecified) {
assembly ("memory-safe") {
amountSpecified := tload(AMOUNT_SPECIFIED_SLOT)
}
}
}

0 comments on commit b1effee

Please sign in to comment.