示例#1
0
def mint_weth_dai_kovan():
    kovan_weth_balance = ERC20Token(web3=web3, address=weth_token_kovan.address).balance_of(account_address)
    kovan_dai_balance = ERC20Token(web3=web3, address=dai_token_kovan.address).balance_of(account_address)

    desired_lower_tick = Tick.nearest_usable_tick(
        weth_dai_kovan_pool.tick_current - (weth_dai_kovan_pool.tick_spacing * 5), weth_dai_kovan_pool.tick_spacing)
    desired_upper_tick = Tick.nearest_usable_tick(
        weth_dai_kovan_pool.tick_current + (weth_dai_kovan_pool.tick_spacing * 3), weth_dai_kovan_pool.tick_spacing)
    slippage_tolerance = Fraction(2, 100)
    deadline = int(time.time() + 1000)

    weth_to_add = kovan_weth_balance / Wad.from_number(2)
    dai_to_add = kovan_dai_balance / Wad.from_number(2)
    kovan_position_to_mint = Position.from_amounts(weth_dai_kovan_pool, desired_lower_tick, desired_upper_tick,
                                                   weth_to_add.value, dai_to_add.value, False)

    print("position_to_mint liquidity", kovan_position_to_mint.liquidity)

    # approve kovan tokens for usage by PositionManager
    position_manager_kovan.approve(weth_token_kovan)
    position_manager_kovan.approve(dai_token_kovan)

    kovan_mint_params = MintParams(web3, NonfungiblePositionManager_abi, kovan_position_to_mint, account_address,
                                   slippage_tolerance, deadline)
    kovan_mint_receipt = position_manager_kovan.mint(kovan_mint_params).transact()

    assert kovan_mint_receipt is not None and kovan_mint_receipt.successful
    print("tx receipt", kovan_mint_receipt.transaction_hash.hex())
示例#2
0
def mint_weth_usdc_kovan():

    # returned balance from balance_of shows amounts normalized to 18 decimals, so unnormalization to USDC 6 decimals is needed
     kovan_weth_balance = ERC20Token(web3=web3, address=weth_token_kovan.address).balance_of(account_address)
     kovan_usdc_balance = ERC20Token(web3=web3, address=usdc_token_kovan.address).balance_of(account_address)

     print("balance from balance_of, WETH: ", kovan_weth_balance, "USDC: ", kovan_usdc_balance)
     current_tick = weth_usdc_kovan_pool.tick_current
     current_pool_price = PriceFraction.get_price_at_tick(weth_token_kovan, usdc_token_kovan, weth_usdc_kovan_pool.tick_current)

     print("current pool tick", weth_usdc_kovan_pool.tick_current)
     print("current pool price", current_pool_price.float_quotient())

     desired_price = PriceFraction(weth_token_kovan, usdc_token_kovan, Wad.from_number(1).value, usdc_token_kovan.unnormalize_amount(Wad.from_number(2800)).value)
     desired_tick = PriceFraction.get_tick_at_price(desired_price)
     print("desired tick and price", desired_tick, desired_price.float_quotient())

     desired_lower_tick = Tick.nearest_usable_tick(
          desired_tick - (weth_usdc_kovan_pool.tick_spacing * 5), weth_usdc_kovan_pool.tick_spacing)
     desired_upper_tick = Tick.nearest_usable_tick(
          desired_tick + (weth_usdc_kovan_pool.tick_spacing * 3), weth_usdc_kovan_pool.tick_spacing)
     slippage_tolerance = Fraction(2, 100)
     deadline = int(time.time() + 1000)

     # weth_to_add = kovan_weth_balance / Wad.from_number(2)
     # usdc_to_add = usdc_token_kovan.normalize_amount(kovan_usdc_balance) / Wad.from_number(2)
     weth_to_add = kovan_weth_balance / Wad.from_number(2)
     usdc_to_add = kovan_usdc_balance / Wad.from_number(2)

     print("pool token 0: ", weth_usdc_kovan_pool.token_0.name)

     print("weth to add: ", weth_to_add, "usdc to add: ", usdc_to_add)
     kovan_position_to_mint = Position.from_amounts(weth_usdc_kovan_pool, desired_lower_tick, desired_upper_tick,
                                                    usdc_to_add.value, weth_to_add.value, False)

     print("position_to_mint liquidity", kovan_position_to_mint.liquidity)

     # approve kovan tokens for usage by PositionManager
     position_manager_kovan.approve(weth_token_kovan)
     position_manager_kovan.approve(usdc_token_kovan)

     kovan_mint_params = MintParams(web3, NonfungiblePositionManager_abi, kovan_position_to_mint, account_address,
                                    slippage_tolerance, deadline)
     kovan_mint_receipt = position_manager_kovan.mint(kovan_mint_params).transact()

     assert kovan_mint_receipt is not None and kovan_mint_receipt.successful
     print("tx receipt", kovan_mint_receipt.transaction_hash.hex())
示例#3
0
    def test_mint_token_pool_low_price_and_slippage(self):
        """ Test minting a position for a pool that is a small fraction """
        test_token_1 = Token(
            "test_1", Address("0x0000000000000000000000000000000000000001"),
            18)
        test_token_2 = Token(
            "test_2", Address("0x0000000000000000000000000000000000000002"),
            18)

        token_1_balance = Wad.from_number(10)
        token_2_balance = Wad.from_number(100)

        # sqrt_price_ratio = self.get_starting_sqrt_ratio(3000, 1)
        sqrt_price_ratio = self.get_starting_sqrt_ratio(
            Wad.from_number(3000).value,
            Wad.from_number(1).value)
        current_tick = get_tick_at_sqrt_ratio(sqrt_price_ratio)
        ticks = []
        test_pool = Pool(test_token_1, test_token_2, FEES.MEDIUM.value,
                         sqrt_price_ratio, 0, current_tick, ticks)

        # set Position.from_amounts() params
        tick_lower = current_tick - TICK_SPACING.MEDIUM.value * 5
        tick_upper = current_tick + TICK_SPACING.MEDIUM.value * 7
        rounded_tick_lower = Tick.nearest_usable_tick(
            tick_lower, TICK_SPACING.MEDIUM.value)
        rounded_tick_upper = Tick.nearest_usable_tick(
            tick_upper, TICK_SPACING.MEDIUM.value)
        calculated_position = Position.from_amounts(
            test_pool, rounded_tick_lower, rounded_tick_upper,
            token_1_balance.value, token_2_balance.value, False)

        test_liquidity = calculated_position.liquidity

        test_position = Position(test_pool, rounded_tick_lower,
                                 rounded_tick_upper, test_liquidity)

        amount_0, amount_1 = test_position.mint_amounts()

        slippage_tolerance = Fraction(2, 100)
        amount_0_min, amount_1_min = test_position.mint_amounts_with_slippage(
            slippage_tolerance)

        # check that mint amounts will pass periphery contract assertions
        assert amount_0_min > 0 and amount_1_min > 0
        assert amount_0_min < amount_0 and amount_1_min < amount_1
示例#4
0
def test_nearest_usable_tick():
    # given
    current_tick = 74999
    tick_spacing = TICK_SPACING.MEDIUM.value

    # when
    rounded_tick = Tick.nearest_usable_tick(current_tick, tick_spacing)

    # then
    assert rounded_tick % tick_spacing == 0
示例#5
0
    def deploy_and_mint_weth_dai(self, position_manager_helpers) -> Pool:
        # deploy weth_dai pool and mint initial liquidity to swap against
        position_manager_helper_wethdai = position_manager_helpers(self.web3, self.position_manager,
                                                                   self.NonfungiblePositionManager_abi, self.token_weth,
                                                                   self.token_dai)

        starting_price = self.get_starting_sqrt_ratio(1, 1900)
        weth_dai_pool = position_manager_helper_wethdai.create_and_initialize_pool(
            starting_price, FEES.MEDIUM.value)

        # wrap weth for testing (univ3 only uses weth)
        position_manager_helper_wethdai.wrap_eth(Wad.from_number(25), self.our_address)



        tick_lower = weth_dai_pool.tick_current - TICK_SPACING.MEDIUM.value * 3
        tick_upper = weth_dai_pool.tick_current + TICK_SPACING.MEDIUM.value * 5
        rounded_tick_lower = Tick.nearest_usable_tick(tick_lower, TICK_SPACING.MEDIUM.value)
        rounded_tick_upper = Tick.nearest_usable_tick(tick_upper, TICK_SPACING.MEDIUM.value)

        # TODO: calculate liquidity at a given price, with available balances
        # current liquidity levels result in too small of a balance being minted - need to determine why higher liquidity fails price slippage check
        liquidity_to_mint = 900000000000000

        weth_dai_mint_params = position_manager_helper_wethdai.generate_mint_params(weth_dai_pool,
                                                                                    Position(weth_dai_pool, rounded_tick_lower, rounded_tick_upper,
                                                                                             9000000000000000000), self.our_address,
                                                                                    Fraction(20, 100))
        weth_dai_mint_receipt = self.position_manager.mint(weth_dai_mint_params).transact()
        assert weth_dai_mint_receipt is not None and weth_dai_mint_receipt.successful

        # mint_result = weth_dai_mint_receipt.result[0]
        # print("mint result", mint_result, mint_result.liquidity, mint_result.tick_lower, mint_result.tick_upper)
        token_id = weth_dai_mint_receipt.result[0].token_id
        print("minted_pool token_id", token_id)
        minted_position = self.position_manager.positions(token_id, weth_dai_pool.token_0, weth_dai_pool.token_1)
        print("minted weth_dai value", self.position_manager.price_position(token_id, 1900))
        return minted_position.pool
示例#6
0
    def test_liquidity_given_balance(self):
        """ Test liquidity and mint amount calculations """
        test_token_1 = Token(
            "test_1", Address("0x0000000000000000000000000000000000000001"),
            18)
        test_token_2 = Token(
            "test_2", Address("0x0000000000000000000000000000000000000002"), 6)

        token_1_balance = test_token_1.unnormalize_amount(Wad.from_number(10))
        token_2_balance = test_token_2.unnormalize_amount(Wad.from_number(500))

        sqrt_price_ratio = self.get_starting_sqrt_ratio(
            Wad.from_number(1).value,
            test_token_2.unnormalize_amount(Wad.from_number(3000)).value)
        current_tick = get_tick_at_sqrt_ratio(sqrt_price_ratio)
        ticks = []
        test_pool = Pool(test_token_1, test_token_2, FEES.MEDIUM.value,
                         sqrt_price_ratio, 0, current_tick, ticks)

        tick_lower = current_tick - TICK_SPACING.MEDIUM.value * 5
        tick_upper = current_tick + TICK_SPACING.MEDIUM.value * 7
        rounded_tick_lower = Tick.nearest_usable_tick(
            tick_lower, TICK_SPACING.MEDIUM.value)
        rounded_tick_upper = Tick.nearest_usable_tick(
            tick_upper, TICK_SPACING.MEDIUM.value)
        calculated_position = Position.from_amounts(
            test_pool, rounded_tick_lower, rounded_tick_upper,
            token_1_balance.value, token_2_balance.value, False)

        test_liquidity = calculated_position.liquidity
        assert test_liquidity == 252860870269028

        test_position = Position(test_pool, rounded_tick_lower,
                                 rounded_tick_upper, test_liquidity)

        amount_0, amount_1 = test_position.mint_amounts()
        assert amount_0 == 95107120950731527
        assert amount_1 == 208677042
示例#7
0
    def test_should_mint_with_nonstandard_decimals(self):
        """ mint a position with one of the tokens having nonstandard decimals.
            Verify that the positions price and minted amounts accounts for decimals.
        """
        test_token_1 = Token(
            "test_1", Address("0x0000000000000000000000000000000000000001"),
            18)
        test_token_2 = Token(
            "test_2", Address("0x0000000000000000000000000000000000000002"), 6)

        # instantiate test pool
        # sqrt_price_ratio = self.get_starting_sqrt_ratio(Wad.from_number(1).value, Wad.from_number(3500).value)
        sqrt_price_ratio = self.get_starting_sqrt_ratio(1, 3500)
        current_tick = get_tick_at_sqrt_ratio(sqrt_price_ratio)
        ticks = []
        test_pool = Pool(test_token_1, test_token_2, FEES.MEDIUM.value,
                         sqrt_price_ratio, 0, current_tick, ticks)

        # based upon current price (expressed in token1/token0), determine the tick to mint the position at
        tick_spacing = TICK_SPACING.MEDIUM.value
        desired_price = PriceFraction(test_token_1, test_token_2, 1, 3500)
        desired_tick = PriceFraction.get_tick_at_price(desired_price)

        # identify upper and lower tick bounds for the position
        desired_lower_tick = Tick.nearest_usable_tick(
            desired_tick - tick_spacing * 5, tick_spacing)
        desired_upper_tick = Tick.nearest_usable_tick(
            desired_tick + tick_spacing * 7, tick_spacing)

        # calculate amount to add for each position.
        ## since test_token_2 has 6 decimals, we must unnormalize the Wad amount from 18 -> 6
        token_1_balance = Wad.from_number(10)
        token_2_balance = Wad.from_number(100)

        token_1_to_add = test_token_1.unnormalize_amount(token_1_balance).value
        token_2_to_add = test_token_2.unnormalize_amount(token_2_balance).value
        # token_1_to_add = token_1_balance.value
        # token_2_to_add = token_2_balance.value

        calculated_position = Position.from_amounts(test_pool,
                                                    desired_lower_tick,
                                                    desired_upper_tick,
                                                    token_1_to_add,
                                                    token_2_to_add, False)

        amount_0, amount_1 = calculated_position.mint_amounts()

        slippage_tolerance = Fraction(2, 100)
        amount_0_min, amount_1_min = calculated_position.mint_amounts_with_slippage(
            slippage_tolerance)

        # check that mint amounts will pass periphery contract assertions
        assert amount_0 > 0 and amount_1 > 0
        assert amount_0_min > 0 and amount_1_min > 0
        assert amount_0_min < amount_0 and amount_1_min < amount_1

        # assume pool.tick_current < desired_upper_tick
        expected_amount_0 = SqrtPriceMath.get_amount_0_delta(
            test_pool.square_root_ratio_x96,
            get_sqrt_ratio_at_tick(desired_upper_tick),
            calculated_position.liquidity, True)
        expected_amount_1 = SqrtPriceMath.get_amount_1_delta(
            get_sqrt_ratio_at_tick(desired_lower_tick),
            test_pool.square_root_ratio_x96, calculated_position.liquidity,
            True)

        assert amount_0 == expected_amount_0
        assert amount_1 == expected_amount_1

        # get amounts from liquidity
        price_lower_tick = pow(1.0001, calculated_position.tick_lower)
        price_upper_tick = pow(1.0001, calculated_position.tick_upper)

        assert price_lower_tick < 3500 < price_upper_tick

        position_token_0 = calculated_position.liquidity / math.sqrt(
            price_upper_tick)
        position_token_1 = calculated_position.liquidity * math.sqrt(
            price_lower_tick)

        # compare original sqrt_price_ratio_x96 to the ratio determined by liquidity to mint
        assert str(sqrt_price_ratio)[:2] == str(
            encodeSqrtRatioX96(int(position_token_1),
                               int(position_token_0)))[:2]
        assert sqrt_price_ratio // Q96 == encodeSqrtRatioX96(
            int(position_token_1), int(position_token_0)) // (2**96)
示例#8
0
    def swap(self, zero_or_one: bool, swap_amount: int,
             sqrt_price_limit_x96: int) -> dict:
        """ Calculate a swap and output pool state
            @param zero_or_one boolean indicating swapping in token_0 or token_1
            @param swap_amount integer amount of the given token to be swapped
            @param sqrt_price_limit_x96 price limit that can't be breached following swap reserve changes
            @returns dictionary of resulting pool state {amount_calculated, sqrt_price_x96, liquidity, tick_current}
        """
        assert isinstance(zero_or_one, bool)
        assert isinstance(swap_amount, int)
        assert (isinstance(sqrt_price_limit_x96, int)
                or sqrt_price_limit_x96 is None)

        if sqrt_price_limit_x96 is None:
            sqrt_price_limit_x96 = MIN_SQRT_RATIO + 1 if zero_or_one else MAX_SQRT_RATIO - 1

        if zero_or_one:
            assert sqrt_price_limit_x96 > MIN_SQRT_RATIO
            assert sqrt_price_limit_x96 < self.square_root_ratio_x96
        else:
            assert sqrt_price_limit_x96 < MAX_SQRT_RATIO
            assert sqrt_price_limit_x96 > self.square_root_ratio_x96

        exact_input = swap_amount >= 0

        pool_swap_state = {
            "swap_amount_remaining": swap_amount,
            "amount_calculated": 0,
            "sqrt_price_x96": self.square_root_ratio_x96,
            "tick": self.tick_current,
            "liquidity": self.liquidity
        }

        # loop through available ticks until the desired swap amount has been met, or available liquidity has been exhausted
        while pool_swap_state["swap_amount_remaining"] != 0 and pool_swap_state[
                "sqrt_price_x96"] != sqrt_price_limit_x96:

            tick_next, tick_initalized = Tick.next_initialized_tick_within_word(
                self.ticks, pool_swap_state["tick"], zero_or_one,
                self.tick_spacing)

            step_state = {
                "sqrt_price_start_x96": pool_swap_state["sqrt_price_x96"],
                "tick_next": tick_next,
                "tick_initalized": tick_initalized
            }

            # check to see if swap would reach the end of the space
            if step_state["tick_next"] < MIN_TICK:
                step_state["tick_next"] = MIN_TICK
            elif step_state["tick_next"] > MAX_TICK:
                step_state["tick_next"] = MAX_TICK

            # identify price at the next tick with available liquidity
            step_state["sqrt_price_next_x96"] = get_sqrt_ratio_at_tick(
                step_state["tick_next"])

            # calculate which target price to use when computing where the next swap will lead the pool state
            if zero_or_one:
                use_price_limit = step_state[
                    "sqrt_price_next_x96"] < sqrt_price_limit_x96
            else:
                use_price_limit = step_state[
                    "sqrt_price_next_x96"] > sqrt_price_limit_x96
            target_price = sqrt_price_limit_x96 if use_price_limit else step_state[
                "sqrt_price_next_x96"]

            pool_swap_state["sqrt_price_x96"], step_state[
                "amount_in"], step_state["amount_out"], step_state[
                    "fee_amount"] = compute_swap_step(
                        pool_swap_state["sqrt_price_x96"], target_price,
                        pool_swap_state["liquidity"],
                        pool_swap_state["swap_amount_remaining"], self.fee)

            if exact_input:
                pool_swap_state["swap_amount_remaining"] = pool_swap_state[
                    "swap_amount_remaining"] - (step_state["amount_in"] +
                                                step_state["fee_amount"])
                pool_swap_state["amount_calculated"] = pool_swap_state[
                    "amount_calculated"] - step_state["amount_out"]
            else:
                pool_swap_state["swap_amount_remaining"] = pool_swap_state[
                    "swap_amount_remaining"] + step_state["amount_out"]
                pool_swap_state["amount_calculated"] = pool_swap_state[
                    "amount_calculated"] + step_state[
                        "amount_in"] + step_state["fee_amount"]

            if pool_swap_state["sqrt_price_x96"] == step_state[
                    "sqrt_price_next_x96"]:
                if step_state["tick_initalized"]:
                    net_liquidity = Tick.get_tick(
                        self.ticks, step_state["tick_next"]).liquidity_net
                    # when moving left on the tick map, liquidity_net becomes negative
                    if zero_or_one:
                        net_liquidity = net_liquidity * -1
                    pool_swap_state["liquidity"] = add_liquidity_delta(
                        pool_swap_state["liquidity"], net_liquidity)

                pool_swap_state["tick"] = step_state[
                    "tick_next"] - 1 if zero_or_one else step_state["tick_next"]
            elif pool_swap_state["sqrt_price_x96"] != step_state[
                    "sqrt_price_start_x96"]:
                pool_swap_state["tick"] = get_tick_at_sqrt_ratio(
                    pool_swap_state["sqrt_price_x96"])

        return {
            "amount_calculated": pool_swap_state["amount_calculated"],
            "sqrt_price_x96": pool_swap_state["sqrt_price_x96"],
            "liquidity": pool_swap_state["liquidity"],
            "tick_current": pool_swap_state["tick"]
        }
示例#9
0
 def _tick_mapper(self, tick) -> Tick:
     if isinstance(tick, Tick):
         return tick
     else:
         return Tick(tick[0], tick[1], tick[2])