# Uniswap Smart Path
#### Project Information
[![Continous Integration](https://github.com/Elnaril/uniswap-smart-path/actions/workflows/ci.yml/badge.svg)](https://github.com/Elnaril/uniswap-smart-path/actions/workflows/ci.yml)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/uniswap-smart-path)](https://pypi.org/project/uniswap-smart-path/)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Elnaril/uniswap-smart-path)](https://github.com/Elnaril/uniswap-smart-path/releases)
[![PyPi Repository](https://img.shields.io/badge/repository-pipy.org-blue)](https://pypi.org/project/uniswap-smart-path/)
[![License](https://img.shields.io/github/license/Elnaril/uniswap-smart-path)](https://github.com/Elnaril/uniswap-smart-path/blob/master/LICENSE)
#### Code Quality
[![Test Coverage](https://img.shields.io/badge/dynamic/json?color=blueviolet&label=coverage&query=%24.totals.percent_covered_display&suffix=%25&url=https%3A%2F%2Fraw.githubusercontent.com%2FElnaril%2Funiswap-smart-path%2Fmaster%2Fcoverage.json)](https://github.com/Elnaril/uniswap-smart-path/blob/master/coverage.json)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
[![Type Checker: mypy](https://img.shields.io/badge/%20type%20checker-mypy-%231674b1?style=flat&labelColor=ef8336)](https://mypy-lang.org/)
[![Linter: flake8](https://img.shields.io/badge/%20linter-flake8-%231674b1?style=flat&labelColor=ef8336)](https://flake8.pycqa.org/en/latest/)
## Release notes
### v0.3.0
* Add Rate Limiter for APIs using credits, CUPS or request units, as well as number of requests per time unit.
* Use [credit-rate-limit](https://github.com/Elnaril/credit-rate-limit) under the hood.
* Remove `eth_call` from the methods that are automatically verified by web3 (to prevent surges of useless `eth_chainId`)
* Add support for Python 3.12 & 3.13
* Add support for web3.py v7
* Miscellaneous fixes/updates for tests and linting
## Overview
When swapping, it is not straightforward to be sure to get the best deal: with several V2 and V3 pools, and 2 or 3 tokens per path, there may be quite a few routes to perform a swap.
The object of this library is to find the path(s), from v2 and v3 pools, to swap with the best price,
and to return it/them in order to be used directly with the [Universal Router codec](https://github.com/Elnaril/uniswap-universal-router-decoder),
along with a percentage to know how to divide the amount between them if there is more than one result.
⚠ To prevent surges of useless `eth_chainId`, `eth_call` is removed from the methods that are automatically verified by web3.
⚠ This tool does not replace your own due diligence to find the best price/path to swap any token.
---
## Installation
A good practice is to use [Python virtual environments](https://python.readthedocs.io/en/latest/library/venv.html), here is a [tutorial](https://www.freecodecamp.org/news/how-to-setup-virtual-environments-in-python/).
The library can be pip installed from [pypi.org](https://pypi.org/project/uniswap-smart-path/) as usual:
```bash
# update pip to latest version if needed
pip install -U pip
# install the decoder from pypi.org
pip install uniswap-smart-path
```
---
## Usage
The library exposes a class, `SmartPath` with a public method `get_swap_in_path()` that can be used to find the best path/price.
For performance's sake, it is asynchronous. Currently, it supports only the Ethereum blockchain.
```python
from uniswap_smart_path import SmartPath
smart_path = await SmartPath.create(async_w3) # where async_w3 is your AsyncWeb3 instance
path = await smart_path.get_swap_in_path(amount_in_wei, token0_address, token1_address)
```
You can also create the `SmartPath` instance from a rpc endpoint:
```python
from uniswap_smart_path import SmartPath
smart_path = await SmartPath.create(rpc_endpoint=rpc_endpoint)
```
### V2 or V3 pools only
The factory method `SmartPath.create_v2_only()` can be used to create a `SmartPath` instance that will look for the best path in V2 pools only.
Currently, it supports only the Ethereum blockchain.
```python
from uniswap_smart_path import SmartPath
smart_path = await SmartPath.create_v2_only(rpc_endpoint=rpc_endpoint) # could also use an AsyncWeb3 instance i/o rpc
```
Same thing if you wish to look into V3 pools only, just use `SmartPath.create_v3_only()`
Currently, it supports only the Ethereum blockchain.
```python
from uniswap_smart_path import SmartPath
smart_path = await SmartPath.create_v3_only(rpc_endpoint=rpc_endpoint) # could also use an AsyncWeb3 instance i/o rpc
```
### Custom pools and blockchains
A custom SmartPath can be created with the factory method `SmartPath.create_custom()`
```python
from uniswap_smart_path import SmartPath
pivot_tokens = (wbnb_address, usdt_address, usdc_address, dai_address) # BSC addresses
v3_pool_fees = (100, 500, 2500, 10000) # Pancakeswap v3 fees
smart_path = await SmartPath.create_custom(
w3, # BSC AsyncWeb3 instance
pivot_tokens=pivot_tokens,
v3_pool_fees=v3_pool_fees,
v2_router=pancakeswapv2_address,
v2_factory=pancakeswapv2_factory,
v3_quoter=pancakeswapv3_quoter_address,
v3_factory=pancakeswapv3_factory,
)
```
### Using a Rate Limiter
It's possible to manage rate limits, though only API calls used to compute the paths are rate limited.
(Only the RPC method `eth_call` is concerned)
API calls performed to create the `SmartPath` objects are not rate limited.
Both `eth_call` and `chain_id` are involved in these creations.
#### Credit Rate Limit
For APIs that use credits, computation unit per second (CUPS) or request units:
```python
from uniswap_smart_path import SmartPath, SmartRateLimiter
# Define a credit limiter allowing 300 credits per second.
# The method 'eth_call' costs 20 credits.
credit_limiter = SmartRateLimiter(interval=1, max_credits=300, method_credits={"eth_call": 20})
smart_path = await SmartPath.create(w3, smart_rate_limiter=credit_limiter)
```
Note that only the `eth_call` method is needed at the moment.
#### Count Rate Limit
For APIs that just count the number of requests per time unit:
```python
from uniswap_smart_path import SmartPath, SmartRateLimiter
# Define a count limiter allowing 5 requests per second.
count_limiter = SmartRateLimiter(interval=1, max_count=5)
smart_path = await SmartPath.create(w3, smart_rate_limiter=count_limiter)
```
## Result
Examples of output paths that you can use with the [UR codec](https://github.com/Elnaril/uniswap-universal-router-decoder) to encode a transaction.
#### Single path
```python
(
{
'path': ('0xD533a949740bb3306d119CC777fa900bA034cd52', 3000, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 3000, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2'),
'function': 'V3_SWAP_EXACT_IN',
'weight': 100,
'estimate': 128331138758276360764
},
)
```
#### Multi path
Here, 90% of the amount should be swapped on a V3 pool and 10% on a V2 pool, all that in a single transaction using the UR codec.
```python
(
{
'path': ('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
'function': 'V2_SWAP_EXACT_IN',
'weight': 10,
'estimate': 32858922292711987411
},
{
'path': ('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', 3000, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
'function': 'V3_SWAP_EXACT_IN',
'weight': 90,
'estimate': 295000857928717844546
}
)
```
Raw data
{
"_id": null,
"home_page": null,
"name": "uniswap-smart-path",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "blockchain, ethereum, uniswap, exchange, dex, universal router, swap, path, route, pools",
"author": null,
"author_email": "Elnaril <elnaril_dev@caramail.com>",
"download_url": "https://files.pythonhosted.org/packages/d7/cc/00de046c269a0202f98fb95e80ee92bc206b24d3c2836f73c6357d79880e/uniswap_smart_path-0.3.0.tar.gz",
"platform": null,
"description": "# Uniswap Smart Path\n\n#### Project Information\n[![Continous Integration](https://github.com/Elnaril/uniswap-smart-path/actions/workflows/ci.yml/badge.svg)](https://github.com/Elnaril/uniswap-smart-path/actions/workflows/ci.yml)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/uniswap-smart-path)](https://pypi.org/project/uniswap-smart-path/)\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Elnaril/uniswap-smart-path)](https://github.com/Elnaril/uniswap-smart-path/releases)\n[![PyPi Repository](https://img.shields.io/badge/repository-pipy.org-blue)](https://pypi.org/project/uniswap-smart-path/)\n[![License](https://img.shields.io/github/license/Elnaril/uniswap-smart-path)](https://github.com/Elnaril/uniswap-smart-path/blob/master/LICENSE)\n\n#### Code Quality\n[![Test Coverage](https://img.shields.io/badge/dynamic/json?color=blueviolet&label=coverage&query=%24.totals.percent_covered_display&suffix=%25&url=https%3A%2F%2Fraw.githubusercontent.com%2FElnaril%2Funiswap-smart-path%2Fmaster%2Fcoverage.json)](https://github.com/Elnaril/uniswap-smart-path/blob/master/coverage.json)\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)\n[![Type Checker: mypy](https://img.shields.io/badge/%20type%20checker-mypy-%231674b1?style=flat&labelColor=ef8336)](https://mypy-lang.org/)\n[![Linter: flake8](https://img.shields.io/badge/%20linter-flake8-%231674b1?style=flat&labelColor=ef8336)](https://flake8.pycqa.org/en/latest/)\n\n## Release notes\n### v0.3.0\n* Add Rate Limiter for APIs using credits, CUPS or request units, as well as number of requests per time unit.\n * Use [credit-rate-limit](https://github.com/Elnaril/credit-rate-limit) under the hood.\n * Remove `eth_call` from the methods that are automatically verified by web3 (to prevent surges of useless `eth_chainId`)\n* Add support for Python 3.12 & 3.13\n* Add support for web3.py v7\n* Miscellaneous fixes/updates for tests and linting\n\n## Overview \n\nWhen swapping, it is not straightforward to be sure to get the best deal: with several V2 and V3 pools, and 2 or 3 tokens per path, there may be quite a few routes to perform a swap.\n\nThe object of this library is to find the path(s), from v2 and v3 pools, to swap with the best price,\nand to return it/them in order to be used directly with the [Universal Router codec](https://github.com/Elnaril/uniswap-universal-router-decoder),\nalong with a percentage to know how to divide the amount between them if there is more than one result.\n\n\u26a0 To prevent surges of useless `eth_chainId`, `eth_call` is removed from the methods that are automatically verified by web3.\n\u26a0 This tool does not replace your own due diligence to find the best price/path to swap any token.\n\n---\n\n## Installation\nA good practice is to use [Python virtual environments](https://python.readthedocs.io/en/latest/library/venv.html), here is a [tutorial](https://www.freecodecamp.org/news/how-to-setup-virtual-environments-in-python/).\n\nThe library can be pip installed from [pypi.org](https://pypi.org/project/uniswap-smart-path/) as usual:\n\n```bash\n# update pip to latest version if needed\npip install -U pip\n\n# install the decoder from pypi.org\npip install uniswap-smart-path\n```\n\n---\n\n## Usage\n\nThe library exposes a class, `SmartPath` with a public method `get_swap_in_path()` that can be used to find the best path/price.\nFor performance's sake, it is asynchronous. Currently, it supports only the Ethereum blockchain.\n\n```python\nfrom uniswap_smart_path import SmartPath\n\nsmart_path = await SmartPath.create(async_w3) # where async_w3 is your AsyncWeb3 instance\npath = await smart_path.get_swap_in_path(amount_in_wei, token0_address, token1_address)\n\n```\n\nYou can also create the `SmartPath` instance from a rpc endpoint:\n```python\nfrom uniswap_smart_path import SmartPath\n\nsmart_path = await SmartPath.create(rpc_endpoint=rpc_endpoint)\n\n```\n\n### V2 or V3 pools only\nThe factory method `SmartPath.create_v2_only()` can be used to create a `SmartPath` instance that will look for the best path in V2 pools only.\nCurrently, it supports only the Ethereum blockchain.\n\n```python\nfrom uniswap_smart_path import SmartPath\n\nsmart_path = await SmartPath.create_v2_only(rpc_endpoint=rpc_endpoint) # could also use an AsyncWeb3 instance i/o rpc\n\n```\n\nSame thing if you wish to look into V3 pools only, just use `SmartPath.create_v3_only()`\nCurrently, it supports only the Ethereum blockchain.\n\n```python\nfrom uniswap_smart_path import SmartPath\n\nsmart_path = await SmartPath.create_v3_only(rpc_endpoint=rpc_endpoint) # could also use an AsyncWeb3 instance i/o rpc\n\n```\n\n### Custom pools and blockchains\nA custom SmartPath can be created with the factory method `SmartPath.create_custom()`\n\n```python\nfrom uniswap_smart_path import SmartPath\n\npivot_tokens = (wbnb_address, usdt_address, usdc_address, dai_address) # BSC addresses\nv3_pool_fees = (100, 500, 2500, 10000) # Pancakeswap v3 fees\n\nsmart_path = await SmartPath.create_custom(\n w3, # BSC AsyncWeb3 instance\n pivot_tokens=pivot_tokens,\n v3_pool_fees=v3_pool_fees,\n v2_router=pancakeswapv2_address,\n v2_factory=pancakeswapv2_factory,\n v3_quoter=pancakeswapv3_quoter_address,\n v3_factory=pancakeswapv3_factory,\n )\n```\n\n### Using a Rate Limiter\nIt's possible to manage rate limits, though only API calls used to compute the paths are rate limited.\n(Only the RPC method `eth_call` is concerned)\n\nAPI calls performed to create the `SmartPath` objects are not rate limited.\nBoth `eth_call` and `chain_id` are involved in these creations.\n\n#### Credit Rate Limit\nFor APIs that use credits, computation unit per second (CUPS) or request units:\n```python\nfrom uniswap_smart_path import SmartPath, SmartRateLimiter\n\n# Define a credit limiter allowing 300 credits per second.\n# The method 'eth_call' costs 20 credits.\ncredit_limiter = SmartRateLimiter(interval=1, max_credits=300, method_credits={\"eth_call\": 20})\nsmart_path = await SmartPath.create(w3, smart_rate_limiter=credit_limiter)\n```\nNote that only the `eth_call` method is needed at the moment.\n\n#### Count Rate Limit\nFor APIs that just count the number of requests per time unit:\n```python\nfrom uniswap_smart_path import SmartPath, SmartRateLimiter\n\n# Define a count limiter allowing 5 requests per second.\ncount_limiter = SmartRateLimiter(interval=1, max_count=5)\nsmart_path = await SmartPath.create(w3, smart_rate_limiter=count_limiter)\n```\n\n## Result\nExamples of output paths that you can use with the [UR codec](https://github.com/Elnaril/uniswap-universal-router-decoder) to encode a transaction.\n\n#### Single path\n```python\n(\n {\n 'path': ('0xD533a949740bb3306d119CC777fa900bA034cd52', 3000, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 3000, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2'),\n 'function': 'V3_SWAP_EXACT_IN',\n 'weight': 100,\n 'estimate': 128331138758276360764\n },\n)\n```\n\n#### Multi path\nHere, 90% of the amount should be swapped on a V3 pool and 10% on a V2 pool, all that in a single transaction using the UR codec.\n```python\n(\n {\n 'path': ('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),\n 'function': 'V2_SWAP_EXACT_IN',\n 'weight': 10,\n 'estimate': 32858922292711987411\n },\n {\n 'path': ('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', 3000, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),\n 'function': 'V3_SWAP_EXACT_IN',\n 'weight': 90,\n 'estimate': 295000857928717844546\n }\n)\n```\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "Find the path from v2 and v3 pools to swap with the best price",
"version": "0.3.0",
"project_urls": {
"Bug Tracker": "https://github.com/Elnaril/uniswap-smart-path/issues",
"Buy Me A Coffee": "https://www.buymeacoffee.com/elnaril",
"Fiverr": "https://www.fiverr.com/freelancers/elnaril",
"Homepage": "https://github.com/Elnaril/uniswap-smart-path",
"Malt": "https://www.malt.com/profile/elnaril"
},
"split_keywords": [
"blockchain",
" ethereum",
" uniswap",
" exchange",
" dex",
" universal router",
" swap",
" path",
" route",
" pools"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a74cdd13f25b9d49a45ddac074c5a82bb44352694173c99a6650b27a1c5c98a2",
"md5": "1716c644bf5f65f2852611bbc5726160",
"sha256": "e747679c973ee6b1a9b2036648990889df5ea396e57b11b1c93f0e5022c69381"
},
"downloads": -1,
"filename": "uniswap_smart_path-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1716c644bf5f65f2852611bbc5726160",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 14116,
"upload_time": "2024-10-27T14:23:30",
"upload_time_iso_8601": "2024-10-27T14:23:30.680046Z",
"url": "https://files.pythonhosted.org/packages/a7/4c/dd13f25b9d49a45ddac074c5a82bb44352694173c99a6650b27a1c5c98a2/uniswap_smart_path-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d7cc00de046c269a0202f98fb95e80ee92bc206b24d3c2836f73c6357d79880e",
"md5": "e04768150f4353fac6ff5b3edf3918f9",
"sha256": "9e3217a1f4f2ba1115de39038e9b28ee7d0469368c71454b790b9ea7e5b0dfda"
},
"downloads": -1,
"filename": "uniswap_smart_path-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "e04768150f4353fac6ff5b3edf3918f9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 18226,
"upload_time": "2024-10-27T14:23:32",
"upload_time_iso_8601": "2024-10-27T14:23:32.421064Z",
"url": "https://files.pythonhosted.org/packages/d7/cc/00de046c269a0202f98fb95e80ee92bc206b24d3c2836f73c6357d79880e/uniswap_smart_path-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-27 14:23:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Elnaril",
"github_project": "uniswap-smart-path",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"tox": true,
"lcname": "uniswap-smart-path"
}