diff --git a/examples/integrations/uniswap/test_funcs.py b/examples/integrations/uniswap/uni_example.py similarity index 80% rename from examples/integrations/uniswap/test_funcs.py rename to examples/integrations/uniswap/uni_example.py index 7906a94..17279e7 100644 --- a/examples/integrations/uniswap/test_funcs.py +++ b/examples/integrations/uniswap/uni_example.py @@ -4,7 +4,7 @@ from dotenv import find_dotenv, load_dotenv import logging -from giza_actions.integrations.uniswap.uniswap import Uniswap +from giza.agents.integrations.uniswap.uniswap import Uniswap load_dotenv(find_dotenv()) dev_passphrase = os.environ.get("DEV_PASSPHRASE") @@ -44,7 +44,7 @@ token0.deposit(value=int(1e18), sender=sender) token0_balance = token0.balanceOf(sender) logger.info(f"--------- Balances before swap: {token0_balance} {token1.balanceOf(sender)}") - token0.approve(uni.router.contract, token0_balance, sender=sender) + # token0.approve(uni.router.contract, token0_balance, sender=sender) uni.swap_exact_input_single( token0_balance, token_in=token0, token_out=token1, fee=fee ) @@ -55,7 +55,7 @@ token0_amount_out = int(1e16) # 0.01 eth accepted_slippage = 0.01 # 1% amount_in_max = int(token1.balanceOf(sender) * (1 - accepted_slippage)) - uni.swap_exact_output_single( + tx = uni.swap_exact_output_single( token0_amount_out, token_in=token1, token_out=token0, @@ -67,16 +67,16 @@ ) ### NFT Manager ### - token0.approve(uni.nft_manager.contract, token0.balanceOf(sender), sender=sender) - token1.approve(uni.nft_manager.contract, token1.balanceOf(sender), sender=sender) + # token0.approve(uni.nft_manager.contract, token0.balanceOf(sender), sender=sender) + # token1.approve(uni.nft_manager.contract, token1.balanceOf(sender), sender=sender) user_positions = uni.get_all_user_positions() logger.info(f"--------- User Positions Init: {user_positions}") amount0_to_mint = int(0.5 * token0.balanceOf(sender)) amount1_to_mint = int(0.5 * token1.balanceOf(sender)) - price = pool.get_pool_price(invert=True) + price = pool.get_pool_price() pct_dev = 0.1 - lower_price = int(price * (1 - pct_dev)) - upper_price = int(price * (1 + pct_dev)) + lower_price = price * (1 - pct_dev) + upper_price = price * (1 + pct_dev) uni.mint_position( pool, lower_price, @@ -90,8 +90,9 @@ slippage_tolerance=1, ) user_positions = uni.get_all_user_positions() + price = pool.get_pool_price() logger.info(f"--------- User Positions after minting: {user_positions}") - nft_id = user_positions[0] + nft_id = user_positions[-1] pos_info = uni.get_pos_info(nft_id) logger.info(f"--------- {nft_id} Info: {pos_info}") increase_fraction = 0.1 @@ -102,6 +103,15 @@ ) pos_info = uni.get_pos_info(nft_id) logger.info(f"--------- {nft_id} Info Add Liq: {pos_info}") + price = pool.get_pool_price() + pct_dev = 0.5 + lower_price = price * (1 - pct_dev) + upper_price = price * (1 + pct_dev) + uni.rebalance_lp(nft_id, lower_price, upper_price) + user_positions = uni.get_all_user_positions() + nft_id = user_positions[-1] + pos_info = uni.get_pos_info(nft_id) + logger.info(f"--------- {nft_id} Info after rebalance: {pos_info}") uni.close_position(nft_id) user_positions = uni.get_all_user_positions() logger.info(f"--------- {nft_id} Burnt, user_pos: {user_positions}") diff --git a/giza_actions/integrations/uniswap/ASSETS/erc20.json b/giza/agents/integrations/uniswap/assets/erc20.json similarity index 100% rename from giza_actions/integrations/uniswap/ASSETS/erc20.json rename to giza/agents/integrations/uniswap/assets/erc20.json diff --git a/giza_actions/integrations/uniswap/ASSETS/nft_manager.json b/giza/agents/integrations/uniswap/assets/nft_manager.json similarity index 100% rename from giza_actions/integrations/uniswap/ASSETS/nft_manager.json rename to giza/agents/integrations/uniswap/assets/nft_manager.json diff --git a/giza_actions/integrations/uniswap/ASSETS/pool.json b/giza/agents/integrations/uniswap/assets/pool.json similarity index 100% rename from giza_actions/integrations/uniswap/ASSETS/pool.json rename to giza/agents/integrations/uniswap/assets/pool.json diff --git a/giza_actions/integrations/uniswap/ASSETS/pool_factory.json b/giza/agents/integrations/uniswap/assets/pool_factory.json similarity index 100% rename from giza_actions/integrations/uniswap/ASSETS/pool_factory.json rename to giza/agents/integrations/uniswap/assets/pool_factory.json diff --git a/giza_actions/integrations/uniswap/ASSETS/quoter.json b/giza/agents/integrations/uniswap/assets/quoter.json similarity index 100% rename from giza_actions/integrations/uniswap/ASSETS/quoter.json rename to giza/agents/integrations/uniswap/assets/quoter.json diff --git a/giza_actions/integrations/uniswap/ASSETS/router.json b/giza/agents/integrations/uniswap/assets/router.json similarity index 100% rename from giza_actions/integrations/uniswap/ASSETS/router.json rename to giza/agents/integrations/uniswap/assets/router.json diff --git a/giza_actions/integrations/uniswap/constants.py b/giza/agents/integrations/uniswap/constants.py similarity index 100% rename from giza_actions/integrations/uniswap/constants.py rename to giza/agents/integrations/uniswap/constants.py diff --git a/giza_actions/integrations/uniswap/nft_manager.py b/giza/agents/integrations/uniswap/nft_manager.py similarity index 76% rename from giza_actions/integrations/uniswap/nft_manager.py rename to giza/agents/integrations/uniswap/nft_manager.py index 7b42e27..998f9ff 100644 --- a/giza_actions/integrations/uniswap/nft_manager.py +++ b/giza/agents/integrations/uniswap/nft_manager.py @@ -3,8 +3,9 @@ from ape import Contract -from giza_actions.integrations.uniswap.constants import MAX_UINT_128 -from giza_actions.integrations.uniswap.utils import ( +from giza.agents.integrations.uniswap.constants import MAX_UINT_128 +from giza.agents.integrations.uniswap.pool import Pool +from giza.agents.integrations.uniswap.utils import ( calc_amount0, calc_amount1, liquidity0, @@ -82,8 +83,18 @@ def add_liquidity( amount1Min: int = 0, deadline: int = None, ): + pos = self.contract.positions(nft_id) + token0 = Contract(pos["token0"], abi=os.path.join(os.path.dirname(__file__), "assets/erc20.json")) + token1 = Contract(pos["token1"], abi=os.path.join(os.path.dirname(__file__), "assets/erc20.json")) + # give allowances if needed + if amount0Desired > token0.allowance(self.sender, self.contract.address): + token0.approve(self.contract.address, amount0Desired, sender=self.sender) + if amount1Desired > token1.allowance(self.sender, self.contract.address): + token1.approve(self.contract.address, amount1Desired, sender=self.sender) + if deadline is None: deadline = int(time.time() + 60) + receipt = self.contract.increaseLiquidity( (nft_id, amount0Desired, amount1Desired, amount0Min, amount1Min, deadline), sender=self.sender, @@ -105,10 +116,12 @@ def mint_position( ): fee = pool.fee token0 = pool.token0 + token0_decimals = token0.decimals() token1 = pool.token1 + token1_decimals = token1.decimals() - lower_tick = price_to_tick(lower_price) - upper_tick = price_to_tick(upper_price) + lower_tick = price_to_tick(lower_price, token0_decimals, token1_decimals) + upper_tick = price_to_tick(upper_price, token0_decimals, token1_decimals) lower_tick = nearest_tick(lower_tick, fee) upper_tick = nearest_tick(upper_tick, fee) @@ -124,6 +137,11 @@ def mint_position( amount0 = calc_amount0(liq, sqrtp_upp, sqrtp_cur) amount1 = calc_amount1(liq, sqrtp_low, sqrtp_cur) + if amount0 > token0.allowance(self.sender, self.contract.address): + token0.approve(self.contract.address, amount0, sender=self.sender) + if amount1 > token1.allowance(self.sender, self.contract.address): + token1.approve(self.contract.address, amount1, sender=self.sender) + if recipient is None: recipient = self.sender if deadline is None: @@ -146,4 +164,6 @@ def mint_position( "recipient": recipient, "deadline": deadline, } + return self.contract.mint(mint_params, sender=self.sender) + \ No newline at end of file diff --git a/giza_actions/integrations/uniswap/pool.py b/giza/agents/integrations/uniswap/pool.py similarity index 93% rename from giza_actions/integrations/uniswap/pool.py rename to giza/agents/integrations/uniswap/pool.py index 1b24dc9..7aff9bc 100644 --- a/giza_actions/integrations/uniswap/pool.py +++ b/giza/agents/integrations/uniswap/pool.py @@ -2,7 +2,7 @@ from ape import Contract, chain -from giza_actions.integrations.uniswap.utils import tick_to_price +from giza.agents.integrations.uniswap.utils import tick_to_price class Pool: @@ -10,8 +10,6 @@ def __init__( self, address: str, sender: str, - token0: str = None, - token1: str = None, fee: int = None, ): self.contract = Contract( diff --git a/giza_actions/integrations/uniswap/pool_factory.py b/giza/agents/integrations/uniswap/pool_factory.py similarity index 82% rename from giza_actions/integrations/uniswap/pool_factory.py rename to giza/agents/integrations/uniswap/pool_factory.py index ed171e2..73ee20f 100644 --- a/giza_actions/integrations/uniswap/pool_factory.py +++ b/giza/agents/integrations/uniswap/pool_factory.py @@ -2,7 +2,7 @@ from ape import Contract -from giza_actions.integrations.uniswap.pool import Pool +from giza.agents.integrations.uniswap.pool import Pool class PoolFactory: @@ -17,7 +17,7 @@ def get_pool(self, token0: str, token1: str, fee: int): if type(fee) == float: fee = int(fee * 1e6) pool_address = self.contract.getPool(token0, token1, fee) - return Pool(pool_address, self.sender, token0=token0, token1=token1, fee=fee) + return Pool(pool_address, self.sender) def create_pool(self, token0: str, token1: str, fee: int): if type(fee) == float: diff --git a/giza_actions/integrations/uniswap/quoter.py b/giza/agents/integrations/uniswap/quoter.py similarity index 100% rename from giza_actions/integrations/uniswap/quoter.py rename to giza/agents/integrations/uniswap/quoter.py diff --git a/giza_actions/integrations/uniswap/router.py b/giza/agents/integrations/uniswap/router.py similarity index 89% rename from giza_actions/integrations/uniswap/router.py rename to giza/agents/integrations/uniswap/router.py index 9129cac..6910859 100644 --- a/giza_actions/integrations/uniswap/router.py +++ b/giza/agents/integrations/uniswap/router.py @@ -3,7 +3,7 @@ from ape import Contract -from giza_actions.integrations.uniswap.pool import Pool +from giza.agents.integrations.uniswap.pool import Pool class Router: @@ -24,8 +24,6 @@ def swap_exact_input_single( sqrt_price_limit_x96: int = 0, deadline: int = None, ): - if deadline is None: - deadline = int(time.time()) + 60 if pool is None and (token_in is None or token_out is None or fee is None): raise Exception("Must provide pool or token_in, token_out, and fee") @@ -35,10 +33,19 @@ def swap_exact_input_single( token_out = pool.token1 if token_out is None else token_out fee = pool.fee if fee is None else fee + if type(token_in)==str: + token_in = Contract(token_in, abi=os.path.join(os.path.dirname(__file__), "assets/erc20.json")) + + if amount_in > token_in.allowance(self.sender, self.contract.address): + token_in.approve(self.contract.address, amount_in, sender=self.sender) + # TODO: # add slippage and pool price impact protection # if amount_out_min or sqrt_price_limit_x96 are floats + if deadline is None: + deadline = int(time.time()) + 60 + swap_params = { "tokenIn": token_in, "tokenOut": token_out, diff --git a/giza_actions/integrations/uniswap/uniswap.py b/giza/agents/integrations/uniswap/uniswap.py similarity index 79% rename from giza_actions/integrations/uniswap/uniswap.py rename to giza/agents/integrations/uniswap/uniswap.py index 45d10cf..9d088f7 100644 --- a/giza_actions/integrations/uniswap/uniswap.py +++ b/giza/agents/integrations/uniswap/uniswap.py @@ -1,11 +1,12 @@ -from ape import chain +from ape import chain, Contract +import os -from giza_actions.integrations.uniswap.constants import ADDRESSES, MAX_UINT_128 -from giza_actions.integrations.uniswap.nft_manager import NFTManager -from giza_actions.integrations.uniswap.pool import Pool -from giza_actions.integrations.uniswap.pool_factory import PoolFactory -from giza_actions.integrations.uniswap.quoter import Quoter -from giza_actions.integrations.uniswap.router import Router +from giza.agents.integrations.uniswap.constants import ADDRESSES, MAX_UINT_128 +from giza.agents.integrations.uniswap.nft_manager import NFTManager +from giza.agents.integrations.uniswap.pool import Pool +from giza.agents.integrations.uniswap.pool_factory import PoolFactory +from giza.agents.integrations.uniswap.quoter import Quoter +from giza.agents.integrations.uniswap.router import Router class Uniswap: @@ -133,6 +134,28 @@ def mint_position( slippage_tolerance=slippage_tolerance, ) + def rebalance_lp( + self, + nft_id: int, + lower_price: float, + upper_price: float, + amount0Min: int = None, + amount1Min: int = None, + recipient: str = None, + deadline: int = None, + slippage_tolerance: float = 1, + ): + pos = self.nft_manager.contract.positions(nft_id) + pool = self.get_pool(pos["token0"], pos["token1"], pos["fee"]) + + token0 = Contract(pos["token0"], abi=os.path.join(os.path.dirname(__file__), "assets/erc20.json")) + token1 = Contract(pos["token1"], abi=os.path.join(os.path.dirname(__file__), "assets/erc20.json")) + self.nft_manager.close_position(nft_id) + amount0 = token0.balanceOf(self.sender) + amount1 = token1.balanceOf(self.sender) + return self.nft_manager.mint_position(pool, lower_price, upper_price, amount0, amount1, amount0Min= amount0Min, amount1Min=amount1Min, recipient=recipient, deadline=deadline, slippage_tolerance=slippage_tolerance) + + def quote_exact_input_single( self, amount_in: int, diff --git a/giza_actions/integrations/uniswap/utils.py b/giza/agents/integrations/uniswap/utils.py similarity index 90% rename from giza_actions/integrations/uniswap/utils.py rename to giza/agents/integrations/uniswap/utils.py index 50bf254..7ba4da7 100644 --- a/giza_actions/integrations/uniswap/utils.py +++ b/giza/agents/integrations/uniswap/utils.py @@ -3,7 +3,7 @@ from ape import Contract -from giza_actions.integrations.uniswap.constants import ( +from giza.agents.integrations.uniswap.constants import ( MAX_TICK, MIN_TICK, Q96, @@ -16,9 +16,8 @@ def load_contract(address): return Contract(address) -def price_to_tick(price): - sqrtPriceX96 = int(price * 2**96) - tick = math.floor(math.log((sqrtPriceX96 / Q96) ** 2) / math.log(TICKS_Q)) +def price_to_tick(price, decimals0, decimals1): + tick = math.floor(math.log(price * 10**(decimals1 - decimals0), TICKS_Q)) return tick