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

Full range hook #22

Merged
merged 53 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5baa429
in progress full range
emmaguo13 Jun 12, 2023
aeca079
rename
emmaguo13 Jun 12, 2023
d9faa8a
in progress
emmaguo13 Jun 12, 2023
0221a35
merge
emmaguo13 Jun 12, 2023
1c129a5
compiling
emmaguo13 Jun 12, 2023
e70cd04
basic tests passing
emmaguo13 Jun 12, 2023
e23c51f
erc20 contract deploy
emmaguo13 Jun 13, 2023
6408697
initial liquidity deposit working
emmaguo13 Jun 14, 2023
3f40d27
removeLiquidity tests
emmaguo13 Jun 15, 2023
c477de5
withdrawing liquidity works
emmaguo13 Jun 16, 2023
366986c
Merge branch 'main' of https://github.com/Uniswap/periphery-next into…
emmaguo13 Jun 20, 2023
7981828
merge with main
emmaguo13 Jun 20, 2023
46284be
in progress
emmaguo13 Jun 20, 2023
93520f4
compiling code with rebalancing
emmaguo13 Jun 21, 2023
d81c9d6
basic liquidity add tests with fee working
emmaguo13 Jun 21, 2023
b45a9b3
in progress add liq swap tests
emmaguo13 Jun 21, 2023
5f6ebd8
debug add liquidity rebalance
emmaguo13 Jun 22, 2023
4a75522
simple rebalance working
emmaguo13 Jun 22, 2023
e4a73f4
removing liquidity tests passing but check implementation
emmaguo13 Jun 23, 2023
0d1aab6
add gas snapshots
emmaguo13 Jul 3, 2023
5455377
add blockNumber and liquidity token addr to poolinfo struct, update g…
emmaguo13 Jul 5, 2023
57fd231
update erc20 contract, rerun gas snapshots, remove create2
emmaguo13 Jul 5, 2023
2ff3320
readd create2, rerun gas snapshots with create2
emmaguo13 Jul 5, 2023
cc5e275
increase amounts used in tests
emmaguo13 Jul 11, 2023
b21c83d
update tests and debug rebalance
emmaguo13 Aug 1, 2023
226f5f9
fixed rebalancing dust issue
emmaguo13 Aug 1, 2023
377be83
working tests and fixed rebalancing
emmaguo13 Aug 1, 2023
8e111ba
Merge remote-tracking branch 'origin' into v2-hook
emmaguo13 Aug 1, 2023
03fa369
fix dependencies
emmaguo13 Aug 1, 2023
a85576c
debug lib
emmaguo13 Aug 2, 2023
aa867da
some code cleanup and v4-core update
emmaguo13 Aug 2, 2023
edc8595
some more cleanup
emmaguo13 Aug 3, 2023
04d9e07
some more code cleanup
emmaguo13 Aug 4, 2023
014578d
Optimize v2 hook (#49)
emmaguo13 Aug 10, 2023
1274a72
comment + test cleanup
emmaguo13 Aug 10, 2023
032027c
deploy erc20 with custom liquidity token name
emmaguo13 Aug 10, 2023
da2b0e6
cleanup and add custom tick spacing error
emmaguo13 Aug 10, 2023
79f5064
add minimum liquidity
emmaguo13 Aug 11, 2023
b6a6369
make rebalance public
emmaguo13 Aug 11, 2023
8ff1391
fuzz testing
emmaguo13 Aug 11, 2023
e807f5c
clean up tests
emmaguo13 Aug 11, 2023
693128a
in progress pr comment addressing
emmaguo13 Aug 14, 2023
1f98ef0
fix burning liquidity, some code cleanup
emmaguo13 Aug 16, 2023
9654a29
uncached storage slot remove liquidity gas test
emmaguo13 Aug 16, 2023
edf4d78
use safe cast library
emmaguo13 Aug 16, 2023
3e21a8f
fix minimum liquidity error, add price slippage test
emmaguo13 Aug 16, 2023
468299c
address pr comments
emmaguo13 Aug 17, 2023
0ab98b7
fix remove liquidity fuzz test
emmaguo13 Aug 17, 2023
fc4feb4
add fuzz test for swapping and removing all liquidity
emmaguo13 Aug 17, 2023
c809051
add univ4 to erc20 token names
emmaguo13 Aug 17, 2023
2c97fb7
update init snapshot
emmaguo13 Aug 17, 2023
ea8b119
fix max tick liquidity
emmaguo13 Aug 17, 2023
8dd517c
custom errors
emmaguo13 Aug 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
469 changes: 469 additions & 0 deletions contracts/hooks/FullRange.sol

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions contracts/hooks/IUniswapV4ERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity >=0.5.0;

interface IUniswapV4ERC20 {
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);

function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);

function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);

// function DOMAIN_SEPARATOR() external view returns (bytes32);
// function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint256);

// function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
}
23 changes: 23 additions & 0 deletions contracts/hooks/Math.sol
emmaguo13 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity =0.8.19;

// a library for performing various math operations

library Math {
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x < y ? x : y;
}

// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
17 changes: 17 additions & 0 deletions contracts/hooks/SafeMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity =0.8.19;

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library SafeMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}

function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}

function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
}
93 changes: 93 additions & 0 deletions contracts/hooks/UniswapV4ERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
pragma solidity =0.8.19;

import "./IUniswapV4ERC20.sol";
import "./SafeMath.sol";
import "forge-std/console.sol";

contract UniswapV4ERC20 is IUniswapV4ERC20 {
using SafeMath for uint256;

string public constant name = "Uniswap V4";
string public constant symbol = "UNI-V4";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

bytes32 public DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
// TODO: permit stuff
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
mapping(address => uint256) public nonces;

constructor() public {
// uint chainId;
// assembly {
// chainId := chainid
// }
// DOMAIN_SEPARATOR = keccak256(
// abi.encode(
// keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
// keccak256(bytes(name)),
// keccak256(bytes('1')),
// chainId,
// address(this)
// )
// );
}

function _mint(address to, uint256 value) external {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}

function _burn(address from, uint256 value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}

function _approve(address owner, address spender, uint256 value) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}

function _transfer(address from, address to, uint256 value) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}

function approve(address spender, uint256 value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}

function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}

function transferFrom(address from, address to, uint256 value) external returns (bool) {
if (allowance[from][msg.sender] != uint256(int256(-1))) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_transfer(from, to, value);
return true;
}

// function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
// require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
// bytes32 digest = keccak256(
// abi.encodePacked(
// '\x19\x01',
// DOMAIN_SEPARATOR,
// keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
// )
// );
// address recoveredAddress = ecrecover(digest, v, r, s);
// require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
// _approve(owner, spender, value);
// }
}
134 changes: 134 additions & 0 deletions contracts/libraries/LiquidityAmounts.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.19;

import "@uniswap/v4-core/contracts/libraries/FullMath.sol";
import "@uniswap/v4-core/contracts/libraries/FixedPoint96.sol";

/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
/// @notice Downcasts uint256 to uint128
/// @param x The uint258 to be downcasted
/// @return y The passed value, downcasted to uint128
function toUint128(uint256 x) private pure returns (uint128 y) {
require((y = uint128(x)) == x);
}

/// @notice Computes the amount of liquidity received for a given amount of token0 and price range
/// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount0 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount0(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint256 amount0)
internal
pure
returns (uint128 liquidity)
{
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96);
return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96));
}

/// @notice Computes the amount of liquidity received for a given amount of token1 and price range
/// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount1 The amount1 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount1(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint256 amount1)
internal
pure
returns (uint128 liquidity)
{
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return toUint128(FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96));
}

/// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount of token0 being sent in
/// @param amount1 The amount of token1 being sent in
/// @return liquidity The maximum amount of liquidity received
function getLiquidityForAmounts(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0,
uint256 amount1
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

if (sqrtRatioX96 <= sqrtRatioAX96) {
liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);

liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
} else {
liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1);
}
}

/// @notice Computes the amount of token0 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
function getAmount0ForLiquidity(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity)
internal
pure
returns (uint256 amount0)
{
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

return FullMath.mulDiv(
uint256(liquidity) << FixedPoint96.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96
) / sqrtRatioAX96;
}

/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1
function getAmount1ForLiquidity(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity)
internal
pure
returns (uint256 amount1)
{
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
}

/// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function getAmountsForLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0, uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

if (sqrtRatioX96 <= sqrtRatioAX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
} else {
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
}
}
}
2 changes: 1 addition & 1 deletion lib/forge-gas-snapshot
Submodule forge-gas-snapshot updated 1 files
+21 −0 LICENSE
2 changes: 1 addition & 1 deletion lib/forge-std
emmaguo13 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
Loading