-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
348 additions
and
3 deletions.
There are no files selected for viewing
Submodule v4-core
updated
20 files
+100 −0 | LICENSE | |
+7 −1 | README.md | |
+0 −1 | foundry.toml | |
+0 −63 | licenses/BUSL_LICENSE | |
+0 −7 | licenses/MIT_LICENSE | |
+1 −1 | snapshots/PoolManagerTest.json | |
+1 −1 | src/ProtocolFees.sol | |
+1 −1 | src/interfaces/external/IERC20Minimal.sol | |
+1 −1 | src/libraries/BitMath.sol | |
+1 −1 | src/libraries/FixedPoint128.sol | |
+1 −1 | src/libraries/FixedPoint96.sol | |
+1 −1 | src/libraries/LPFeeLibrary.sol | |
+1 −1 | src/libraries/LiquidityMath.sol | |
+1 −1 | src/libraries/ParseBytes.sol | |
+1 −1 | src/libraries/ProtocolFeeLibrary.sol | |
+1 −1 | src/libraries/SafeCast.sol | |
+1 −1 | src/libraries/SqrtPriceMath.sol | |
+1 −1 | src/libraries/SwapMath.sol | |
+1 −1 | src/libraries/TickMath.sol | |
+1 −1 | src/libraries/UnsafeMath.sol |
Submodule v4-periphery
updated
95 files
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.24; | ||
|
||
import "@uniswap/v4-core/src/libraries/StateLibrary.sol"; | ||
import {IPositionManager} from "@uniswap/v4-periphery/src/interfaces/IPositionManager.sol"; | ||
import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; | ||
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; | ||
|
||
|
||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {console} from "forge-std/console.sol"; | ||
|
||
import {ClvrHook} from "../src/ClvrHook.sol"; | ||
import {EasyPosm} from "./utils/EasyPosm.sol"; | ||
|
||
|
||
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; | ||
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; | ||
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; | ||
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; | ||
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; | ||
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; | ||
import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; | ||
import {CurrencyLibrary, Currency} from "@uniswap/v4-core/src/types/Currency.sol"; | ||
import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol"; | ||
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; | ||
|
||
import {LiquidityAmounts} from "@uniswap/v4-core/test/utils/LiquidityAmounts.sol"; | ||
import {IPositionManager} from "@uniswap/v4-periphery/src/interfaces/IPositionManager.sol"; | ||
|
||
contract ClvrHookTest is Test, Deployers { | ||
using StateLibrary for IPoolManager; | ||
|
||
PoolId poolId; | ||
ClvrHook hook; | ||
|
||
function setUp() public { | ||
deployFreshManagerAndRouters(); | ||
deployMintAndApprove2Currencies(); | ||
|
||
address flags = address( | ||
uint160( | ||
Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_DONATE_FLAG | ||
) ^ (0x4444 << 144) // Namespace the hook to avoid collisions | ||
); | ||
|
||
bytes memory constructorArgs = abi.encode(manager); | ||
deployCodeTo("ClvrHook.sol:ClvrHook", constructorArgs, flags); | ||
hook = ClvrHook(flags); | ||
|
||
key = PoolKey(currency0, currency1, 3000, 60, IHooks(hook)); | ||
poolId = key.toId(); | ||
manager.initialize(key, SQRT_PRICE_1_1); | ||
} | ||
|
||
function testHook() public { | ||
console.log("Hello, World!"); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
pragma solidity ^0.8.21; | ||
|
||
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; | ||
import {BalanceDelta, toBalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; | ||
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; | ||
import {IPositionManager} from "v4-periphery/src/interfaces/IPositionManager.sol"; | ||
import {Actions} from "v4-periphery/src/libraries/Actions.sol"; | ||
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; | ||
import {PositionInfo, PositionInfoLibrary} from "v4-periphery/src/libraries/PositionInfoLibrary.sol"; | ||
|
||
/// @title Easy Position Manager | ||
/// @notice A library for abstracting Position Manager calldata | ||
/// @dev Useable onchain, but expensive because of encoding | ||
library EasyPosm { | ||
using CurrencyLibrary for Currency; | ||
using SafeCast for uint256; | ||
using SafeCast for int256; | ||
using PositionInfoLibrary for PositionInfo; | ||
|
||
/// @dev packing data to avoid stack too deep error | ||
struct MintData { | ||
uint256 balance0Before; | ||
uint256 balance1Before; | ||
bytes[] params; | ||
} | ||
|
||
/// @dev This function supports sending native tokens (ETH), the amount-to-pay is determined by amount0Max. | ||
/// Any excess amount is NOT refunded since it is not encoding the SWEEP action | ||
function mint( | ||
IPositionManager posm, | ||
PoolKey memory poolKey, | ||
int24 tickLower, | ||
int24 tickUpper, | ||
uint256 liquidity, | ||
uint256 amount0Max, | ||
uint256 amount1Max, | ||
address recipient, | ||
uint256 deadline, | ||
bytes memory hookData | ||
) internal returns (uint256 tokenId, BalanceDelta delta) { | ||
(Currency currency0, Currency currency1) = (poolKey.currency0, poolKey.currency1); | ||
|
||
MintData memory mintData = MintData({ | ||
balance0Before: currency0.balanceOf(address(this)), | ||
balance1Before: currency1.balanceOf(address(this)), | ||
params: new bytes[](2) | ||
}); | ||
mintData.params[0] = | ||
abi.encode(poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, recipient, hookData); | ||
mintData.params[1] = abi.encode(currency0, currency1); | ||
|
||
// Mint Liquidity | ||
tokenId = posm.nextTokenId(); | ||
uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0; | ||
posm.modifyLiquidities{value: valueToPass}( | ||
abi.encode(abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR)), mintData.params), | ||
deadline | ||
); | ||
|
||
delta = toBalanceDelta( | ||
-(mintData.balance0Before - currency0.balanceOf(address(this))).toInt128(), | ||
-(mintData.balance1Before - currency1.balanceOf(address(this))).toInt128() | ||
); | ||
} | ||
|
||
function increaseLiquidity( | ||
IPositionManager posm, | ||
uint256 tokenId, | ||
uint256 liquidityToAdd, | ||
uint256 amount0Max, | ||
uint256 amount1Max, | ||
uint256 deadline, | ||
bytes memory hookData | ||
) internal returns (BalanceDelta delta) { | ||
(Currency currency0, Currency currency1) = getCurrencies(posm, tokenId); | ||
|
||
bytes[] memory params = new bytes[](3); | ||
params[0] = abi.encode(tokenId, liquidityToAdd, amount0Max, amount1Max, hookData); | ||
params[1] = abi.encode(currency0); | ||
params[2] = abi.encode(currency1); | ||
|
||
uint256 balance0Before = currency0.balanceOf(address(this)); | ||
uint256 balance1Before = currency1.balanceOf(address(this)); | ||
|
||
uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0; | ||
posm.modifyLiquidities{value: valueToPass}( | ||
abi.encode( | ||
abi.encodePacked( | ||
uint8(Actions.INCREASE_LIQUIDITY), uint8(Actions.CLOSE_CURRENCY), uint8(Actions.CLOSE_CURRENCY) | ||
), | ||
params | ||
), | ||
deadline | ||
); | ||
|
||
delta = toBalanceDelta( | ||
(currency0.balanceOf(address(this)).toInt256() - balance0Before.toInt256()).toInt128(), | ||
(currency1.balanceOf(address(this)).toInt256() - balance1Before.toInt256()).toInt128() | ||
); | ||
} | ||
|
||
function decreaseLiquidity( | ||
IPositionManager posm, | ||
uint256 tokenId, | ||
uint256 liquidityToRemove, | ||
uint256 amount0Min, | ||
uint256 amount1Min, | ||
address recipient, | ||
uint256 deadline, | ||
bytes memory hookData | ||
) internal returns (BalanceDelta delta) { | ||
(Currency currency0, Currency currency1) = getCurrencies(posm, tokenId); | ||
|
||
bytes[] memory params = new bytes[](2); | ||
params[0] = abi.encode(tokenId, liquidityToRemove, amount0Min, amount1Min, hookData); | ||
params[1] = abi.encode(currency0, currency1, recipient); | ||
|
||
uint256 balance0Before = currency0.balanceOf(address(this)); | ||
uint256 balance1Before = currency1.balanceOf(address(this)); | ||
|
||
posm.modifyLiquidities( | ||
abi.encode(abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR)), params), deadline | ||
); | ||
|
||
delta = toBalanceDelta( | ||
(currency0.balanceOf(address(this)) - balance0Before).toInt128(), | ||
(currency1.balanceOf(address(this)) - balance1Before).toInt128() | ||
); | ||
} | ||
|
||
function collect( | ||
IPositionManager posm, | ||
uint256 tokenId, | ||
uint256 amount0Min, | ||
uint256 amount1Min, | ||
address recipient, | ||
uint256 deadline, | ||
bytes memory hookData | ||
) internal returns (BalanceDelta delta) { | ||
(Currency currency0, Currency currency1) = getCurrencies(posm, tokenId); | ||
|
||
bytes[] memory params = new bytes[](2); | ||
// collecting fees is achieved by decreasing liquidity with 0 liquidity removed | ||
params[0] = abi.encode(tokenId, 0, amount0Min, amount1Min, hookData); | ||
params[1] = abi.encode(currency0, currency1, recipient); | ||
|
||
uint256 balance0Before = currency0.balanceOf(recipient); | ||
uint256 balance1Before = currency1.balanceOf(recipient); | ||
|
||
posm.modifyLiquidities( | ||
abi.encode(abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR)), params), deadline | ||
); | ||
|
||
delta = toBalanceDelta( | ||
(currency0.balanceOf(recipient) - balance0Before).toInt128(), | ||
(currency1.balanceOf(recipient) - balance1Before).toInt128() | ||
); | ||
} | ||
|
||
function burn( | ||
IPositionManager posm, | ||
uint256 tokenId, | ||
uint256 amount0Min, | ||
uint256 amount1Min, | ||
address recipient, | ||
uint256 deadline, | ||
bytes memory hookData | ||
) internal returns (BalanceDelta delta) { | ||
(Currency currency0, Currency currency1) = getCurrencies(posm, tokenId); | ||
|
||
bytes[] memory params = new bytes[](2); | ||
params[0] = abi.encode(tokenId, 0, amount0Min, amount1Min, hookData); | ||
params[1] = abi.encode(currency0, currency1, recipient); | ||
|
||
uint256 balance0Before = currency0.balanceOf(recipient); | ||
uint256 balance1Before = currency1.balanceOf(recipient); | ||
|
||
posm.modifyLiquidities( | ||
abi.encode(abi.encodePacked(uint8(Actions.BURN_POSITION), uint8(Actions.TAKE_PAIR)), params), deadline | ||
); | ||
|
||
delta = toBalanceDelta( | ||
(currency0.balanceOf(recipient) - balance0Before).toInt128(), | ||
(currency1.balanceOf(recipient) - balance1Before).toInt128() | ||
); | ||
} | ||
|
||
function getCurrencies(IPositionManager posm, uint256 tokenId) | ||
internal | ||
view | ||
returns (Currency currency0, Currency currency1) | ||
{ | ||
(PoolKey memory key,) = posm.getPoolAndPositionInfo(tokenId); | ||
return (key.currency0, key.currency1); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// // SPDX-License-Identifier: MIT | ||
// pragma solidity ^0.8.24; | ||
|
||
// import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; | ||
// import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; | ||
// import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; | ||
// import {PositionManager} from "v4-periphery/src/PositionManager.sol"; | ||
// import {IPositionManager} from "v4-periphery/src/interfaces/IPositionManager.sol"; | ||
// import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; | ||
// import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; | ||
// import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; | ||
// import {IERC20} from "forge-std/interfaces/IERC20.sol"; | ||
// import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; | ||
// import {DeployPermit2} from "./forks/DeployPermit2.sol"; | ||
// import {IERC721Permit_v4} from "v4-periphery/src/interfaces/IERC721Permit_v4.sol"; | ||
// import {IEIP712_v4} from "v4-periphery/src/interfaces/IEIP712_v4.sol"; | ||
// import {ERC721PermitHash} from "v4-periphery/src/libraries/ERC721PermitHash.sol"; | ||
// import {IPositionDescriptor} from "v4-periphery/src/interfaces/IPositionDescriptor.sol"; | ||
// import {IWETH9} from "v4-periphery/src/interfaces/external/IWETH9.sol"; | ||
|
||
// /// @notice A shared test contract that wraps the v4-core deployers contract and exposes basic liquidity operations on posm. | ||
// contract Fixtures is Deployers, DeployPermit2 { | ||
// uint256 constant STARTING_USER_BALANCE = 10_000_000 ether; | ||
// uint256 constant MAX_SLIPPAGE_ADD_LIQUIDITY = type(uint256).max; | ||
// uint256 constant MAX_SLIPPAGE_REMOVE_LIQUIDITY = 0; | ||
|
||
// IPositionManager posm; | ||
|
||
// function deployAndApprovePosm(IPoolManager poolManager) public { | ||
// deployPosm(poolManager); | ||
// approvePosm(); | ||
// } | ||
|
||
// function deployPosm(IPoolManager poolManager) internal { | ||
// // We use vm.etch to prevent having to use via-ir in this repository. | ||
// etchPermit2(); | ||
// posm = IPositionManager(new PositionManager(poolManager, permit2, 300_000, IPositionDescriptor(address(0)), IWETH9(address(0)))); | ||
// } | ||
|
||
// function seedBalance(address to) internal { | ||
// IERC20(Currency.unwrap(currency0)).transfer(to, STARTING_USER_BALANCE); | ||
// IERC20(Currency.unwrap(currency1)).transfer(to, STARTING_USER_BALANCE); | ||
// } | ||
|
||
// function approvePosm() internal { | ||
// approvePosmCurrency(currency0); | ||
// approvePosmCurrency(currency1); | ||
// } | ||
|
||
// function approvePosmCurrency(Currency currency) internal { | ||
// // Because POSM uses permit2, we must execute 2 permits/approvals. | ||
// // 1. First, the caller must approve permit2 on the token. | ||
// IERC20(Currency.unwrap(currency)).approve(address(permit2), type(uint256).max); | ||
// // 2. Then, the caller must approve POSM as a spender of permit2. TODO: This could also be a signature. | ||
// permit2.approve(Currency.unwrap(currency), address(posm), type(uint160).max, type(uint48).max); | ||
// } | ||
|
||
// // Does the same approvals as approvePosm, but for a specific address. | ||
// function approvePosmFor(address addr) internal { | ||
// vm.startPrank(addr); | ||
// approvePosm(); | ||
// vm.stopPrank(); | ||
// } | ||
|
||
// function permit(uint256 privateKey, uint256 tokenId, address operator, uint256 nonce) internal { | ||
// bytes32 digest = getDigest(operator, tokenId, 1, block.timestamp + 1); | ||
|
||
// (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); | ||
// bytes memory signature = abi.encodePacked(r, s, v); | ||
|
||
// vm.prank(operator); | ||
// IERC721Permit_v4(address(posm)).permit(operator, tokenId, block.timestamp + 1, nonce, signature); | ||
// } | ||
|
||
// function getDigest(address spender, uint256 tokenId, uint256 nonce, uint256 deadline) | ||
// internal | ||
// view | ||
// returns (bytes32 digest) | ||
// { | ||
// digest = keccak256( | ||
// abi.encodePacked( | ||
// "\x19\x01", | ||
// IEIP712_v4(address(posm)).DOMAIN_SEPARATOR(), | ||
// keccak256(abi.encode(ERC721PermitHash.PERMIT_TYPEHASH, spender, tokenId, nonce, deadline)) | ||
// ) | ||
// ); | ||
// } | ||
// } |