GeezerVison is showing github.com — Multihop feature request · Issue #69 · uniswap-python/uniswap-python
Multihop feature request #69DescriptionHey I've noticed that the prices are always displayed for direct routing, in many cases this returns very bad prices with high slippages.
Is there already a method to get the most efficient routing or could you implement it? Thanks! ActivityErikBjare commented on Mar 7, 2021 shanefontaine commented on Mar 8, 2021 I believe the Uniswap router logic (off-chain) that chooses the path is closed source, unfortunately.
With that said, I cannot find any data that confirms or denies this. I will try to figure out if it is open source today. MoneroCrusher commented on Mar 9, 2021 Thanks for the quick feedback guys!
I'd think it would be open source since all the clones & forks also have the routing implemented (Quickswap and Sushiswap for example). liquid-8 commented on Mar 11, 2021 Reacted by MoneroCrusher and BI-NOP MoneroCrusher commented on Mar 19, 2021 ErikBjare commented on Mar 20, 2021 Yup, waiting for someone who wants this to make a PR! liquid-8 commented on Mar 22, 2021 To my regret I can't make pr myself because my implementation differs too much but here is sort of the summary.
Let's say we need the optimal route for swap (token0,token1). Please consider that any of them might be eth/weth.
First, uniswap engine calls useAllCommonPairs(...) to get the list of possible routes. It doesn't contain EVERY possible route, just common ones. Key tokens are listed in BASES_TO_CHECK_TRADES_AGAINST. So, after that call we have list of the routes looking like [token0, basetoken0,token1], [token0, basetoken1,token1] etc. Please note that increasing hops count lead to dramatic growth of that list.
Second, we pass the list to bestTradeExactIn/Out(...) accordingly that analizes routes one by one via recursive calls and returns list of N routes sorted from the best to to worst.
Sure, this is not too easy to implement but also not THAT hard. Also remember that BASES_TO_CHECK_TRADES_AGAINST list varies from one network to another if you need alternate networks support like BSC/Heco/whatever.
Hope that helps, regards. Reacted by Ihor and Amirhosein Soofi dustiwe commented on Mar 23, 2021
To my regret I can't make pr myself because my implementation differs too much but here is sort of the summary.
Let's say we need the optimal route for swap (token0,token1). Please consider that any of them might be eth/weth.
First, uniswap engine calls useAllCommonPairs(...) to get the list of possible routes. It doesn't contain EVERY possible route, just common ones. Key tokens are listed in BASES_TO_CHECK_TRADES_AGAINST. So, after that call we have list of the routes looking like [token0, basetoken0,token1], [token0, basetoken1,token1] etc. Please note that increasing hops count lead to dramatic growth of that list.
Second, we pass the list to bestTradeExactIn/Out(...) accordingly that analizes routes one by one via recursive calls and returns list of N routes sorted from the best to to worst.
Sure, this is not too easy to implement but also not THAT hard. Also remember that BASES_TO_CHECK_TRADES_AGAINST list varies from one network to another if you need alternate networks support like BSC/Heco/whatever.
Hope that helps, regards.
thanks, I will think ErikBjare commented on May 4, 2021 I added initial (largely untested) support for this in #58.
Closing this issue, opened #93 for finding the optimal trade route. qraxiss commented on Mar 11, 2023 My solution is here. +- 1 in 1000 margin of error.
If a PR is requested, I can write a more professional one. But for me this is enough.
from concurrent.futures import ThreadPoolExecutor
from collections import defaultdict
from itertools import permutations
from uniswap import Uniswap
from web3 import Web3
class Token:
def __init__(self, address, name, symbol, decimals) -> None:
self.address = Web3.toChecksumAddress(address)
self._raw_address = address
self.name = name
self.symbol = symbol
self.decimals = decimals
self.qty = 10 ** self.decimals
class UniswapPrice:
sides = {
'sell': 'get_price_output',
'buy': 'get_price_input'
}
percentages = [50, 100]
fees = [100, 300, 3000, 10000]
def __init__(self, v2, v3):
self.swap = {
'v2': {
'exchange': v2,
'percentages': self.percentages,
'fees': [3000]
},
'v3': {
'exchange': v3,
'percentages': self.percentages,
'fees': self.fees
}
}
def find_best(self):
prices = [perm[0] + perm[1] for perm in permutations(
self.pool_prices('different'), 2)] + self.pool_prices('single')
if len(prices) == 0:
return
if self.side == 'sell':
return min(prices)
elif self.side == 'buy':
return max(prices)
def get_price(self, exchange, fee, qty):
queue = [self.t0.address, self.t1.address] if self.side == 'buy' else [
self.t1.address, self.t0.address]
price_func = getattr(exchange, self.sides[self.side])
return price_func(*queue, qty, fee=fee) / self.t1.qty
def pool_prices(self, price_type):
if price_type == 'single':
perc = 100
elif price_type == 'different':
perc = 50
return [
fee[perc]
for exchanges in self.futures.values()
for fee in exchanges.values()
if isinstance(fee[perc], float)
]
def get_best_price(self, t0, t1, side):
self.t0 = t0
self.t1 = t1
self.side = side
self.get_prices()
return self.find_best()
def get_prices(self):
self.futures = defaultdict(dict)
with ThreadPoolExecutor(max_workers=10) as executor:
for exchange_name, exchange_data in self.swap.items():
self.futures[exchange_name] = defaultdict(dict)
for fee in exchange_data['fees']:
self.futures[exchange_name][fee] = defaultdict(dict)
for percentage in exchange_data['percentages']:
self.futures[exchange_name][fee][percentage] = defaultdict(
dict)
qty = self.t0.qty * percentage // 100
self.futures[exchange_name][fee][percentage] = executor.submit(
self.get_price, exchange_data['exchange'], fee, qty
)
for exchange_name, exchange_fees in self.futures.items():
self.futures[exchange_name] = dict(exchange_fees)
for fee, percentage_data in exchange_fees.items():
self.futures[exchange_name][fee] = dict(percentage_data)
for percentage, future in percentage_data.items():
try:
self.futures[exchange_name][fee][percentage] = future.result(
)
except:
pass
web3 = Web3(Web3.HTTPProvider("YOUR-JSON-RPC-URL"))
uni_v2 = Uniswap(
address=None,
private_key=None,
version=2,
web3=web3
)
uni_v3 = Uniswap(
address=None,
private_key=None,
version=3,
web3=web3
)
t0 = Token(**{
"symbol": "ETH",
"name": "Wrapped Ethereum",
"decimals": 18,
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
)
t1 = Token(**{
"symbol": "WBTC",
"name": "Wrapped Bitcoin",
"decimals": 8,
"address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"
}
)
price_calculator = UniswapPrice(uni_v2, uni_v3)
print(price_calculator.get_best_price(t0, t1, 'buy'),
price_calculator.get_best_price(t0, t1, 'sell'))
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|