Skip to content

Commit

Permalink
Merge pull request #18 from uniswapfoundation/quoter
Browse files Browse the repository at this point in the history
Quoter
  • Loading branch information
saucepoint authored Jan 8, 2024
2 parents 58ed895 + 41adb7f commit 819accd
Show file tree
Hide file tree
Showing 21 changed files with 409 additions and 103 deletions.
1 change: 1 addition & 0 deletions .forge-snapshots/counter.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
339161
1 change: 1 addition & 0 deletions .forge-snapshots/hookFee.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
246486
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
src/pages/template/index.html.ts

# foundry
cache/
out/
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "forge-lib/v4-core"]
path = forge-lib/v4-core
url = https://github.com/uniswap/v4-core
[submodule "forge-lib/v4-periphery"]
path = forge-lib/v4-periphery
url = https://github.com/uniswap/v4-periphery
1 change: 1 addition & 0 deletions forge-lib/v4-periphery
Submodule v4-periphery added at 63d64f
29 changes: 23 additions & 6 deletions forge-test/Counter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
import {Constants} from "v4-core/../test/utils/Constants.sol";
import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol";
import {HookTest} from "./utils/HookTest.sol";
import {HookTest} from "@v4-by-example/utils/HookTest.sol";
import {Counter} from "./Counter.sol";
import {HookMiner} from "./utils/HookMiner.sol";
import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol";

contract CounterTest is HookTest {
contract CounterTest is HookTest, GasSnapshot {
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;

Expand All @@ -28,10 +30,8 @@ contract CounterTest is HookTest {
HookTest.initHookTestEnv();

// Deploy the hook to an address with the correct flags
uint160 flags = uint160(
Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG
| Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG
);
uint160 flags =
uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG);
(address hookAddress, bytes32 salt) =
HookMiner.find(address(this), flags, type(Counter).creationCode, abi.encode(address(manager)));
counter = new Counter{salt: salt}(IPoolManager(address(manager)));
Expand Down Expand Up @@ -73,4 +73,21 @@ contract CounterTest is HookTest {
assertEq(counter.beforeSwapCount(poolId), 1);
assertEq(counter.afterSwapCount(poolId), 1);
}

function test_counter_snapshot() public {
int256 amount = 1e18;
bool zeroForOne = true;
IPoolManager.SwapParams memory params = IPoolManager.SwapParams({
zeroForOne: zeroForOne,
amountSpecified: amount,
sqrtPriceLimitX96: zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT // unlimited impact
});

PoolSwapTest.TestSettings memory testSettings =
PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false});

snapStart("counter");
swapRouter.swap(poolKey, params, testSettings, ZERO_BYTES);
snapEnd();
}
}
2 changes: 1 addition & 1 deletion forge-test/CustomCurve.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
import {Constants} from "v4-core/../test/utils/Constants.sol";
import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol";
import {HookTest} from "./utils/HookTest.sol";
import {HookTest} from "@v4-by-example/utils/HookTest.sol";
import {CustomCurve} from "@v4-by-example/pages/hooks/custom-curve/CustomCurve.sol";
import {HookMiner} from "./utils/HookMiner.sol";

Expand Down
23 changes: 21 additions & 2 deletions forge-test/FixedHookFee.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
import {Constants} from "v4-core/../test/utils/Constants.sol";
import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol";
import {HookTest} from "./utils/HookTest.sol";
import {HookTest} from "@v4-by-example/utils/HookTest.sol";
import {FixedHookFee} from "@v4-by-example/pages/fees/fixed-hook-fee/FixedHookFee.sol";
import {HookMiner} from "./utils/HookMiner.sol";
import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol";

contract FixedHookFeeTest is HookTest {
contract FixedHookFeeTest is HookTest, GasSnapshot {
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;

Expand Down Expand Up @@ -70,4 +72,21 @@ contract FixedHookFeeTest is HookTest {
hook.collectFee(alice, Currency.wrap(address(token0)));
assertEq(token0.balanceOf(alice), hook.FIXED_HOOK_FEE());
}

function test_snap_hookFee() public {
int256 amount = 1e18;
bool zeroForOne = true;
IPoolManager.SwapParams memory params = IPoolManager.SwapParams({
zeroForOne: zeroForOne,
amountSpecified: amount,
sqrtPriceLimitX96: zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT // unlimited impact
});

PoolSwapTest.TestSettings memory testSettings =
PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false});

snapStart("hookFee");
swapRouter.swap(poolKey, params, testSettings, ZERO_BYTES);
snapEnd();
}
}
2 changes: 1 addition & 1 deletion forge-test/NoOpSwap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
import {Constants} from "v4-core/../test/utils/Constants.sol";
import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol";
import {HookTest} from "./utils/HookTest.sol";
import {HookTest} from "@v4-by-example/utils/HookTest.sol";
import {NoOpSwap} from "@v4-by-example/pages/hooks/no-op/NoOpSwap.sol";
import {HookMiner} from "./utils/HookMiner.sol";

Expand Down
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ hardhat/=forge-lib/v4-core/node_modules/hardhat/
openzeppelin-contracts/=forge-lib/v4-core/lib/openzeppelin-contracts/contracts/
solmate/=forge-lib/v4-core/lib/solmate/src/
v4-core/=forge-lib/v4-core/src/
@uniswap/v4-core/=forge-lib/v4-core/
9 changes: 9 additions & 0 deletions src/keywords.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
"trade",
"swapping"
],
"/quoter": [
"quoter",
"quoting",
"exact input",
"exact output",
"single",
"multi",
"multihop"
],
"/initialize": [
"pool",
"initialize",
Expand Down
4 changes: 4 additions & 0 deletions src/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export const SOL_ROUTES: Route[] = [
path: "swap",
title: "Swap",
},
{
path: "quoter",
title: "Quoter"
}
]

export const HOOK_ROUTES: Route[] = [
Expand Down
88 changes: 88 additions & 0 deletions src/pages/quoter/Quoter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import {IHooks} from "v4-core/interfaces/IHooks.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";
import {TickMath} from "v4-core/libraries/TickMath.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
import {Constants} from "v4-core/../test/utils/Constants.sol";
import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol";
import {HookTest} from "@v4-by-example/utils/HookTest.sol";
import {IQuoter} from "v4-periphery/interfaces/IQuoter.sol";
import {Quoter} from "v4-periphery/lens/Quoter.sol";

contract QuoterTest is HookTest {
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;

PoolKey poolKey;
PoolId poolId;
Quoter quoter;

function setUp() public {
// creates the pool manager, test tokens, and other utility routers
HookTest.initHookTestEnv();
quoter = new Quoter(address(manager));

// Create the pool
poolKey =
PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 3000, 60, IHooks(address(0x0)));
poolId = poolKey.toId();
initializeRouter.initialize(poolKey, Constants.SQRT_RATIO_1_1, ZERO_BYTES);

// Provide liquidity to the pool
modifyPositionRouter.modifyLiquidity(
poolKey,
IPoolManager.ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 1000 ether),
ZERO_BYTES
);
}

function testQuoter_output() public {
uint128 amountIn = 1e18;
bool zeroForOne = true;
uint160 MAX_SLIPPAGE = zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT;

// get the quote
PoolKey memory key = poolKey;
(int128[] memory deltaAmounts, uint160 sqrtPriceX96After,) = quoter.quoteExactInputSingle(
IQuoter.QuoteExactSingleParams(key, zeroForOne, address(this), amountIn, MAX_SLIPPAGE, ZERO_BYTES)
);

// output is amount 1
int128 outputAmount = deltaAmounts[1];
console2.log("Quoted output amount: ", int256(outputAmount));

// Perform a test swap
BalanceDelta swapDelta = swap(poolKey, int256(uint256(amountIn)), zeroForOne, ZERO_BYTES);

// quote agrees with the actual swap
assertEq(outputAmount, swapDelta.amount1());
}

function testQuoter_input() public {
uint128 amountOut = 1e18;
bool zeroForOne = true;
uint160 MAX_SLIPPAGE = zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT;

// get the quote
PoolKey memory key = poolKey;
(int128[] memory deltaAmounts, uint160 sqrtPriceX96After,) = quoter.quoteExactOutputSingle(
IQuoter.QuoteExactSingleParams(key, zeroForOne, address(this), amountOut, MAX_SLIPPAGE, ZERO_BYTES)
);

// input (quoted) is amount 0
int128 inputAmount = deltaAmounts[0];
console2.log("Quoted input amount: ", int256(inputAmount));

// Perform a exact-output swap
BalanceDelta swapDelta = swap(poolKey, -int256(uint256(amountOut)), zeroForOne, ZERO_BYTES);
assertEq(inputAmount, swapDelta.amount0());
(uint160 sqrtPriceX96,,) = manager.getSlot0(poolId);
assertEq(sqrtPriceX96After, sqrtPriceX96);
}
}
13 changes: 13 additions & 0 deletions src/pages/quoter/QuoterSnippet.solsnippet
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {IQuoter} from "v4-periphery/interfaces/IQuoter.sol";

PoolKey memory key;
uint128 amountIn = 1e18;
bool zeroForOne = true;
uint160 MAX_SLIPPAGE = zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT;
bytes memory hookData;

// exact input will quote deltaAmounts[1] (output)
// exact output will quote deltaAmounts[0] (input)
(int128[] memory deltaAmounts, uint160 sqrtPriceX96After,) = quoter.quoteExactInputSingle(
IQuoter.QuoteExactSingleParams(key, zeroForOne, address(this), amountIn, MAX_SLIPPAGE, hookData)
);
Loading

0 comments on commit 819accd

Please sign in to comment.