From 8ba713a40b94848c0881afd14605bd44166710a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 25 May 2021 22:12:52 +0200 Subject: [PATCH] feat: started working on multihop swaps for v3 --- uniswap/uniswap.py | 5 +-- uniswap/util.py | 80 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/uniswap/uniswap.py b/uniswap/uniswap.py index fc9f1eb..126ee75 100644 --- a/uniswap/uniswap.py +++ b/uniswap/uniswap.py @@ -252,13 +252,14 @@ def _get_token_token_input_price( if self.version == 2: price: int = self.router.functions.getAmountsOut(qty, route).call()[-1] elif self.version == 3: + # FIXME: How to calculate this properly? See https://docs.uniswap.org/reference/libraries/SqrtPriceMath + sqrtPriceLimitX96 = 0 + if route: # NOTE: to support custom routes we need to support the Path data encoding: https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/Path.sol # result: tuple = self.quoter.functions.quoteExactInput(route, qty).call() raise Exception("custom route not yet supported for v3") - # FIXME: How to calculate this properly? See https://docs.uniswap.org/reference/libraries/SqrtPriceMath - sqrtPriceLimitX96 = 0 price = self.quoter.functions.quoteExactInputSingle( token0, token1, fee, qty, sqrtPriceLimitX96 ).call() diff --git a/uniswap/util.py b/uniswap/util.py index 7f3b903..e84b498 100644 --- a/uniswap/util.py +++ b/uniswap/util.py @@ -1,9 +1,11 @@ import os import json import functools -from typing import Union, List, Tuple +from typing import Union, List, Tuple, Any, Dict +from dataclasses import dataclass from web3 import Web3 +from eth_abi import encode_abi from .types import AddressLike, Address, Contract from .exceptions import InvalidToken @@ -57,10 +59,82 @@ def _load_contract_erc20(w3: Web3, address: AddressLike) -> Contract: return _load_contract(w3, "erc20", address) -def _encode_path(token_in: AddressLike, route: List[Tuple[int, AddressLike]]) -> bytes: +@dataclass +class Pool(dict): + token0: AddressLike + token1: AddressLike + fee: int + + +@dataclass +class Route: + pools: List[Pool] + + +def _token_seq_to_route(tokens: List[AddressLike], fee: int = 3000) -> Route: + return Route( + pools=[ + Pool(token0, token1, fee) for token0, token1 in zip(tokens[:-1], tokens[1:]) + ] + ) + + +def _encode_path( + token_in: AddressLike, + route: List[Tuple[int, AddressLike]], + # route: Route, + exactOutput: bool, +) -> bytes: """ Needed for multi-hop swaps in V3. https://github.com/Uniswap/uniswap-v3-sdk/blob/1a74d5f0a31040fec4aeb1f83bba01d7c03f4870/src/utils/encodeRouteToPath.ts """ - raise NotImplementedError + from functools import reduce + + _route = _token_seq_to_route([token_in] + [token for fee, token in route]) + + def merge(acc: Dict[str, Any], pool: Pool) -> Dict[str, Any]: + """Returns a dict with the keys: inputToken, path, types""" + index = 0 if not acc["types"] else None + inputToken = acc["inputToken"] + outputToken = pool.token1 if pool.token0 == inputToken else pool.token0 + if index == 0: + return { + "inputToken": outputToken, + "types": ["address", "uint24", "address"], + "path": [inputToken, pool.fee, outputToken], + } + else: + return { + "inputToken": outputToken, + "types": [*acc["types"], "uint24", "address"], + "path": [*path, pool.fee, outputToken], + } + + params = reduce( + merge, + _route.pools, + {"inputToken": _addr_to_str(token_in), "path": [], "types": []}, + ) + types = params["types"] + path = params["path"] + + if exactOutput: + encoded: bytes = encode_abi(list(reversed(types)), list(reversed(path))) + else: + encoded = encode_abi(types, path) + + return encoded + + +def test_encode_path() -> None: + """Take tests from: https://github.com/Uniswap/uniswap-v3-sdk/blob/1a74d5f0a31040fec4aeb1f83bba01d7c03f4870/src/utils/encodeRouteToPath.test.ts""" + from uniswap.tokens import tokens + + # TODO: Actually assert testcases + path = _encode_path(tokens["WETH"], [(3000, tokens["DAI"])], exactOutput=True) + print(path) + + path = _encode_path(tokens["WETH"], [(3000, tokens["DAI"])], exactOutput=False) + print(path) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy