Skip to content

Commit

Permalink
Merge pull request #29 from Elnaril/v1.0.0.dev0
Browse files Browse the repository at this point in the history
Merge V1.0.0.dev0 into master
  • Loading branch information
Elnaril authored Oct 26, 2023
2 parents 36fb951 + b0c1271 commit 72bbf3b
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 32 deletions.
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@
---

## Release Notes
### v1.0.0
- Add support for SWEEP and PAY_PORTION
- Fix decoding issues
- Remove useless parameter `payer_is_sender` from `v*_swap_exact_in_from_balance()` methods
- Update Router ABI
- Add uint48 and uint160 in ABI builder
### V0.9.1
- Fix lint error
- Change v*_swap_exact_in_from_balance payer_is_sender default to False. This parameter will be removed in the next version.
- Change `v*_swap_exact_in_from_balance()` `payer_is_sender` default to False. This parameter will be removed in the next version.
### V0.9.0
- Add support for UNWRAP_WETH encoding
- Add v2_swap_exact_in_from_balance() and v3_swap_exact_in_from_balance(): 2 convenient methods which are used when the exact in_amount is not known when the transaction is built, typically chained after a V*_SWAP_EXACT_IN.
### V0.8.0
- Breaking changes because of refactoring
- Command chaining extension: all supported UR functions can now be chained in a single transaction
- Add `v2_swap_exact_in_from_balance()` and `v3_swap_exact_in_from_balance()`: 2 convenient methods which are used when the exact in_amount is not known when the transaction is built, typically chained after a `V*_SWAP_EXACT_IN`.

---

Expand All @@ -45,7 +48,10 @@ on Ethereum Mainnet). It is based on, and is intended to be used with [web3.py](
| ---------- | ------------- |:------:|:------:
| 0x00 | V3_SWAP_EXACT_IN | ✅ | ✅
| 0x01 | V3_SWAP_EXACT_OUT | ✅ | ✅
| 0x02 - 0x06 | | ❌ | ❌
| 0x02 - 0x03 | | ❌ | ❌
| 0x04 | SWEEP | ✅ | ✅
| 0x05 | TRANSFER | ❌ | ❌
| 0x06 | PAY_PORTION | ✅ | ✅
| 0x07 | placeholder | N/A | N/A
| 0x08 | V2_SWAP_EXACT_IN | ✅ | ✅
| 0x09 | V2_SWAP_EXACT_OUT | ✅ | ✅
Expand Down Expand Up @@ -162,7 +168,7 @@ The result is a tuple, starting with the "in-token" and ending with the "out-tok


### How to encode
The UR allows the chaining of several functions in the same transaction.
The Universal Router allows the chaining of several functions in the same transaction.
This codec supports it (at least for supported functions) and exposes public methods that can be chained.

The chaining starts with the `encode.chain()` method and ends with the `build()` one which return the full encoded data to be included in the transaction.
Expand Down Expand Up @@ -214,6 +220,7 @@ transaction["data"] = encoded_data

# you can now sign and send the transaction to the UR
```
For more details, see this [tutorial](https://hackernoon.com/how-to-buy-a-token-on-the-uniswap-universal-router-with-python)

### How to encode a call to the function V2_SWAP_EXACT_OUT
This function can be used to swap tokens on a V2 pool. Correct allowances must have been set before sending such transaction.
Expand Down Expand Up @@ -282,8 +289,9 @@ transaction["data"] = encoded_data
```

### How to encode a call to the function PERMIT2_PERMIT
This function is used to give an allowance to the universal router thanks to the Permit2 contract (([`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)).
This function is used to give an allowance to the universal router thanks to the Permit2 contract ([`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)).
It is also necessary to approve the Permit2 contract using the token approve function.
See this [tutorial](https://hackernoon.com/python-how-to-use-permit2-with-the-uniswap-universal-router)
```python
from uniswap_universal_router_decoder import RouterCodec

Expand Down
2 changes: 1 addition & 1 deletion coverage.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"meta": {"version": "7.2.7", "timestamp": "2023-08-04T15:17:52.837067", "branch_coverage": false, "show_contexts": false}, "files": {"uniswap_universal_router_decoder/__init__.py": {"executed_lines": [1, 2, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_abi_builder.py": {"executed_lines": [1, 8, 10, 14, 21, 23, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 38, 39, 42, 43, 44, 45, 48, 51, 52, 53, 55, 56, 57, 59, 60, 61, 63, 64, 65, 67, 68, 69, 71, 72, 74, 75, 76, 78, 79, 80, 82, 83, 84, 87, 88, 89, 99, 101, 102, 103, 104, 105, 107, 108, 109, 110, 111, 113, 114, 115, 116, 117, 118, 119, 120, 122, 123, 124, 125, 127, 128, 129, 130, 131, 133, 134, 135, 136, 138, 139, 140, 141, 142, 144, 145, 146, 147, 148], "summary": {"covered_lines": 93, "num_statements": 93, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_constants.py": {"executed_lines": [1, 8, 13, 16, 17, 18, 20], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_decoder.py": {"executed_lines": [1, 8, 9, 17, 18, 19, 26, 27, 28, 31, 32, 33, 34, 35, 37, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 69, 70, 71, 72, 73, 75, 76, 78, 79, 88, 89, 90, 91, 92, 93, 94, 101, 103, 104, 106], "summary": {"covered_lines": 49, "num_statements": 49, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_encoder.py": {"executed_lines": [1, 8, 10, 11, 22, 23, 24, 25, 26, 27, 28, 34, 35, 40, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 71, 75, 78, 79, 80, 81, 82, 83, 84, 86, 87, 90, 95, 96, 97, 99, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114, 116, 117, 118, 119, 120, 122, 135, 136, 137, 138, 140, 141, 142, 143, 144, 146, 159, 160, 161, 162, 164, 171, 172, 173, 174, 175, 177, 197, 198, 199, 210, 212, 232, 241, 248, 249, 250, 251, 252, 254, 274, 275, 276, 287, 289, 296, 297, 298, 299, 300, 301, 303, 324, 325, 326, 337, 339, 360, 369, 376, 377, 378, 379, 380, 381, 383, 404, 405, 406, 417, 419, 423, 428, 429, 430, 431, 432, 434, 446, 447, 455, 457, 466, 471], "summary": {"covered_lines": 144, "num_statements": 144, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_enums.py": {"executed_lines": [1, 10, 12, 13, 16, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 30, 33, 35, 36, 37], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/router_codec.py": {"executed_lines": [1, 8, 9, 16, 20, 21, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 46, 47, 49, 50, 54, 56, 57, 61, 63, 64, 68, 70, 71, 101, 107, 112, 113, 114, 115], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "totals": {"covered_lines": 353, "num_statements": 353, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}}
{"meta": {"version": "7.3.2", "timestamp": "2023-10-26T12:01:24.826201", "branch_coverage": false, "show_contexts": false}, "files": {"uniswap_universal_router_decoder/__init__.py": {"executed_lines": [1, 2, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_abi_builder.py": {"executed_lines": [1, 8, 10, 14, 21, 23, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 38, 39, 42, 43, 44, 45, 48, 51, 52, 53, 55, 56, 57, 59, 60, 61, 63, 65, 66, 67, 69, 70, 71, 73, 74, 75, 77, 78, 79, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 97, 98, 99, 111, 113, 114, 115, 116, 117, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130, 131, 132, 134, 135, 136, 137, 139, 140, 141, 142, 143, 145, 146, 147, 148, 150, 151, 152, 153, 154, 156, 157, 158, 159, 160, 162, 163, 164, 165, 167, 168, 169, 170], "summary": {"covered_lines": 108, "num_statements": 108, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_constants.py": {"executed_lines": [1, 8, 13, 16, 17, 18, 20], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_decoder.py": {"executed_lines": [1, 8, 9, 17, 18, 19, 26, 27, 28, 31, 32, 33, 34, 35, 37, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 69, 70, 71, 72, 73, 75, 76, 78, 79, 88, 89, 90, 91, 92, 93, 94, 101, 103, 104, 106], "summary": {"covered_lines": 49, "num_statements": 49, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_encoder.py": {"executed_lines": [1, 8, 10, 11, 22, 23, 24, 25, 26, 27, 28, 34, 35, 40, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 71, 75, 78, 79, 80, 81, 82, 83, 84, 86, 87, 90, 95, 96, 97, 99, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114, 116, 117, 118, 119, 120, 122, 135, 136, 137, 138, 140, 141, 142, 143, 144, 146, 159, 160, 161, 162, 164, 171, 172, 173, 174, 175, 177, 197, 198, 199, 210, 212, 230, 239, 246, 247, 248, 249, 250, 252, 272, 273, 274, 285, 287, 294, 295, 296, 297, 298, 299, 301, 322, 323, 324, 335, 337, 356, 365, 372, 373, 374, 375, 376, 377, 379, 400, 401, 402, 413, 415, 419, 424, 425, 426, 427, 428, 430, 442, 443, 451, 453, 454, 455, 456, 457, 459, 474, 475, 476, 485, 487, 488, 489, 490, 491, 493, 509, 514, 516, 517, 518, 527, 529, 538, 543], "summary": {"covered_lines": 166, "num_statements": 166, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/_enums.py": {"executed_lines": [1, 10, 12, 13, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 29, 30, 31, 32, 35, 37, 38, 39], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "uniswap_universal_router_decoder/router_codec.py": {"executed_lines": [1, 8, 9, 16, 20, 21, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 46, 47, 49, 50, 54, 56, 57, 61, 63, 64, 68, 70, 71, 101, 107, 112, 113, 114, 115], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "totals": {"covered_lines": 392, "num_statements": 392, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}}
4 changes: 2 additions & 2 deletions integration_tests/main_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def buy_usdc_from_v2_and_sell_to_v3():
.chain()
.wrap_eth(FunctionRecipient.ROUTER, v2_in_amount)
.v2_swap_exact_in(FunctionRecipient.ROUTER, v2_in_amount, v2_out_amount, v2_path, payer_is_sender=False)
.v3_swap_exact_in_from_balance(FunctionRecipient.ROUTER, v3_out_amount, v3_path, payer_is_sender=False)
.v3_swap_exact_in_from_balance(FunctionRecipient.ROUTER, v3_out_amount, v3_path)
.unwrap_weth(FunctionRecipient.SENDER, 0)
.build(codec.get_default_deadline())
)
Expand Down Expand Up @@ -166,7 +166,7 @@ def buy_usdc_from_v3_and_sell_to_v2():
.chain()
.wrap_eth(FunctionRecipient.ROUTER, v3_in_amount)
.v3_swap_exact_in(FunctionRecipient.ROUTER, v3_in_amount, v3_out_amount, v3_path, payer_is_sender=False)
.v2_swap_exact_in_from_balance(FunctionRecipient.ROUTER, v2_out_amount, v2_path, payer_is_sender=False)
.v2_swap_exact_in_from_balance(FunctionRecipient.ROUTER, v2_out_amount, v2_path)
.unwrap_weth(FunctionRecipient.SENDER, 0)
.build(codec.get_default_deadline())
)
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/main_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def buy_usdc():
.chain()
.wrap_eth(FunctionRecipient.ROUTER, amount_in)
# can chain one of the 2 following v3 swap functions:
.v3_swap_exact_in_from_balance(FunctionRecipient.SENDER, amount_out_min, v3_path, payer_is_sender=False)
.v3_swap_exact_in_from_balance(FunctionRecipient.SENDER, amount_out_min, v3_path)
# .v3_swap_exact_in(FunctionRecipient.SENDER, amount_in, amount_out_min, v3_path, payer_is_sender=False)
.build(codec.get_default_deadline())
)
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/main_3_bsc.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def buy_usdt():
.chain()
.wrap_eth(FunctionRecipient.ROUTER, amount_in)
# can chain one of the 2 following v3 swap functions:
.v3_swap_exact_in_from_balance(FunctionRecipient.SENDER, amount_out_min, v3_path, payer_is_sender=False)
.v3_swap_exact_in_from_balance(FunctionRecipient.SENDER, amount_out_min, v3_path)
# .v3_swap_exact_in(FunctionRecipient.SENDER, amount_in, amount_out_min, v3_path, payer_is_sender=False)
.build(codec.get_default_deadline())
)
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "uniswap-universal-router-decoder"
version = "0.9.1"
version = "1.0.0"
authors = [
{ name="Elnaril", email="[email protected]" },
]
Expand Down Expand Up @@ -35,4 +35,6 @@ packages = ["uniswap_universal_router_decoder"]
[project.urls]
"Homepage" = "https://github.com/Elnaril/uniswap-universal-router-decoder"
"Bug Tracker" = "https://github.com/Elnaril/uniswap-universal-router-decoder/issues"
"Fiverr" = "https://www.fiverr.com/elnaril"
"Discussions" = "https://github.com/Elnaril/uniswap-universal-router-decoder/discussions"
"Fiverr" = "https://www.fiverr.com/freelancers/elnaril"
"Buy Me A Coffee" = "https://www.buymeacoffee.com/elnaril"
4 changes: 2 additions & 2 deletions tests/test_abi_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@


expected_fct_abi_08 = json.loads('{"inputs":[{"name":"recipient","type":"address"},{"name":"amountIn","type":"uint256"},{"name":"amountOutMin","type":"uint256"},{"name":"path","type":"address[]"},{"name":"payerIsSender","type":"bool"}],"name":"V2_SWAP_EXACT_IN","type":"function"}') # noqa
expected_fct_abi_10 = json.loads('{"inputs":[{"name":"struct","type":"tuple","components":[{"name":"details","type":"tuple","components":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"nonce","type":"uint256"}]},{"name":"spender","type":"address"},{"name":"sigDeadline","type":"uint256"}]},{"name":"data","type":"bytes"}],"name":"PERMIT2_PERMIT","type":"function"}') # noqa
expected_fct_abi_10 = json.loads('{"inputs":[{"name":"struct","type":"tuple","components":[{"name":"details","type":"tuple","components":[{"name":"token","type":"address"},{"name":"amount","type":"uint160"},{"name":"expiration","type":"uint48"},{"name":"nonce","type":"uint48"}]},{"name":"spender","type":"address"},{"name":"sigDeadline","type":"uint256"}]},{"name":"data","type":"bytes"}],"name":"PERMIT2_PERMIT","type":"function"}') # noqa


@pytest.mark.parametrize(
"command_id, expected_fct_abi, expected_selector",
(
(_RouterFunction(8), expected_fct_abi_08, bytes.fromhex("3bd2d879")),
(_RouterFunction(10), expected_fct_abi_10, b'\xe5\xa0\x93%'),
(_RouterFunction(10), expected_fct_abi_10, b'9#\xf7\x04'),
)
)
def test_build_abi_map(command_id, expected_fct_abi, expected_selector, codec):
Expand Down
18 changes: 17 additions & 1 deletion tests/test_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,19 @@
expected_function_names_04 = ("WRAP_ETH", "V2_SWAP_EXACT_OUT", "V3_SWAP_EXACT_OUT", "UNWRAP_WETH")

trx_hash_05 = HexStr("0x47c0f1dd13edf9f1608f9f34bdba9ad40cb95dd081033cad69f5b88e451b4b55")
expected_function_names_05 = (None, None)
expected_function_names_05 = (None, "SWEEP")

trx_hash_06 = HexStr("0xe648089f71b2d2e7b70bdcbfdcfeecce6c5248b8eb64b2c79089b7c74c835a45")
expected_function_names_06 = ("V3_SWAP_EXACT_IN", "V3_SWAP_EXACT_IN", "V3_SWAP_EXACT_IN", "SWEEP")

trx_hash_07 = HexStr("0xb5a64e9935b46282080d9198f4478a4c4c1993d590eab8daa0f220c0dca5fe33")
expected_function_names_07 = ("V3_SWAP_EXACT_IN", "PAY_PORTION", "UNWRAP_WETH")

trx_hash_08 = HexStr("0x62176a906ef7f178814a0924d390082053bd8992c2902f436756194693644c21")
expected_function_names_08 = ("WRAP_ETH", "V2_SWAP_EXACT_OUT", "PAY_PORTION", "SWEEP", "UNWRAP_WETH")

trx_hash_09 = HexStr("0x2b6af8ef8fe18829a0fcf2b0f391c55daf76f53bb68369ecaefdb1f38045f919")
expected_function_names_09 = ("PERMIT2_PERMIT", "V2_SWAP_EXACT_IN", "V2_SWAP_EXACT_IN", "V3_SWAP_EXACT_IN", "V2_SWAP_EXACT_IN", "V3_SWAP_EXACT_IN", "SWEEP") # noqa


@pytest.mark.parametrize(
Expand All @@ -37,6 +49,10 @@
(trx_hash_03, w3_instance, None, expected_function_names_03),
(trx_hash_04, w3_instance, None, expected_function_names_04),
(trx_hash_05, w3_instance, None, expected_function_names_05),
(trx_hash_06, w3_instance, None, expected_function_names_06),
(trx_hash_07, w3_instance, None, expected_function_names_07),
(trx_hash_08, w3_instance, None, expected_function_names_08),
(trx_hash_09, w3_instance, None, expected_function_names_09),
)
)
def test_decode_transaction(trx_hash, w3, rpc_endpoint, expected_fct_names):
Expand Down
Loading

0 comments on commit 72bbf3b

Please sign in to comment.