Repository: Elnaril/uniswap-universal-router-decoder (language: python)

Issue:
Decoding issues
NonEmptyPaddingBytes:
Error while decoding trx 0x2b6af8ef8fe18829a0fcf2b0f391c55daf76f53bb68369ecaefdb1f38045f919: Padding bytes were not empty: b'\xc7v\x95\x0f%\x02\xe1\xb7+\xe9\xb2B'
Error while decoding trx 0x806b881b751047a0b84c83d0d4024720f7f736c1c468603bde85a10765567752: Padding bytes were not empty: b'-.\x1b(\x1c\x88\xd9q\x7fS\x88\xaf'
Error while decoding trx 0xabe719c531418ad0c738009ed9e8c545ac0e318ee7482f53439fedfa3d291913: Padding bytes were not empty: b"/\x87.\xca\x08'[pz\x84\xc8w"
Error while decoding trx 0xcab39d096db15a84a0e589e9092fd582ae5c7743c5c296cee3a11d5aa6f070d5: Padding bytes were not empty: b'\x9cw\xfdG\xb6L\xcf|\x14\x19\xce\xc5'

OverflowError:
Error while decoding trx 0x89b6a2a724654f6659dff686a06620a7ebf31cb3a0b708584a5ee36f68a89ddf: cannot fit 'int' into an index-sized integer
Error while decoding trx 0x62198643b5b561689ecde5c47bbed0485de95d8a06205ca8652ddbd9e084389c: cannot fit 'int' into an index-sized integer
Error while decoding trx 0xaa09578416502a1b8ae25cb44d4a4b91853ad6a58e635a92eefd348f83adc41d: cannot fit 'int' into an index-sized integer
Error while decoding trx 0xc94556b28c28b65d34aa54fb5d2bbfdd502253c7aaf01bc3d087ba7ac045f94d: cannot fit 'int' into an index-sized integer

InsufficientDataBytes:
Error while decoding trx 0xd3e1a0ca9c4bc9378e27205aaa0cec7753bb8b315e0ec6bbcf5a7e765a492f13: Tried to read 32 bytes.  Only got 0 bytes
Error while decoding trx 0x5d2ce09546ae92f725a1d3913cca696690f846b78c3e47adca584b84dd49b171: Tried to read 32 bytes.  Only got 0 bytes
Error while decoding trx 0xa5f1f2fd11f4761bfd1a06966a82fa09e060960aacd445bc30b5042559a7b49c: Tried to read 32 bytes.  Only got 0 bytes
Error while decoding trx 0x6b437b6468eb0f48b49f5bc2779539a25122503bfaff4906558d9df245987be1: Tried to read 32 bytes.  Only got 0 bytes



Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 83a5d7b..6d2becd 100644
--- a/README.md
+++ b/README.md
@@ -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`.
 
 ---
 
@@ -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 | ✅ | ✅
@@ -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.
@@ -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.
@@ -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
 
diff --git a/coverage.json b/coverage.json
index 5e1ffc0..e1ab85a 100644
--- a/coverage.json
+++ b/coverage.json
@@ -1,1 +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}}
\ No newline at end of file
+{"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}}
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index a3c2240..a820a98 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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="elnaril_dev@caramail.com" },
 ]
@@ -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"
diff --git a/uniswap_universal_router_decoder/_abi_builder.py b/uniswap_universal_router_decoder/_abi_builder.py
index 56cffcc..54ac2f2 100644
--- a/uniswap_universal_router_decoder/_abi_builder.py
+++ b/uniswap_universal_router_decoder/_abi_builder.py
@@ -56,10 +56,20 @@ def add_address(self, arg_name: str) -> _FunctionABIBuilder:
         self.abi.inputs.append({"name": arg_name, "type": "address"})
         return self
 
-    def add_int(self, arg_name: str) -> _FunctionABIBuilder:
+    def add_uint256(self, arg_name: str) -> _FunctionABIBuilder:
         self.abi.inputs.append({"name": arg_name, "type": "uint256"})
         return self
 
+    add_int = add_uint256
+
+    def add_uint160(self, arg_name: str) -> _FunctionABIBuilder:
+        self.abi.inputs.append({"name": arg_name, "type": "uint160"})
+        return self
+
+    def add_uint48(self, arg_name: str) -> _FunctionABIBuilder:
+        self.abi.inputs.append({"name": arg_name, "type": "uint48"})
+        return self
+
     def add_address_array(self, arg_name: str) -> _FunctionABIBuilder:
         self.abi.inputs.append({"name": arg_name, "type": "address[]"})
         return self
@@ -95,6 +105,8 @@ def build_abi_map(self) -> _ABIMap:
             _RouterFunction.PERMIT2_PERMIT: self._add_mapping(self._build_permit2_permit),
             _RouterFunction.WRAP_ETH: self._add_mapping(self._build_wrap_eth),
             _RouterFunction.UNWRAP_WETH: self._add_mapping(self._build_unwrap_weth),
+            _RouterFunction.SWEEP: self._add_mapping(self._build_sweep),
+            _RouterFunction.PAY_PORTION: self._add_mapping(self._build_pay_portion),
         }
         return abi_map
 
@@ -114,7 +126,7 @@ def _build_v2_swap_exact_in() -> _FunctionABI:
     def _build_permit2_permit() -> _FunctionABI:
         builder = _FunctionABIBuilder("PERMIT2_PERMIT")
         inner_struct = builder.create_struct("details")
-        inner_struct.add_address("token").add_int("amount").add_int("expiration").add_int("nonce")
+        inner_struct.add_address("token").add_uint160("amount").add_uint48("expiration").add_uint48("nonce")
         outer_struct = builder.create_struct("struct")
         outer_struct.add_struct(inner_struct).add_address("spender").add_int("sigDeadline")
         return builder.add_struct(outer_struct).add_bytes("data").build()
@@ -146,3 +158,13 @@ def _build_v3_swap_exact_out() -> _FunctionABI:
         builder = _FunctionABIBuilder("V3_SWAP_EXACT_OUT")
         builder.add_address("recipient").add_int("amountOut").add_int("amountInMax").add_bytes("path")
         return builder.add_bool("payerIsSender").build()
+
+    @staticmethod
+    def _build_sweep() -> _FunctionABI:
+        builder = _FunctionABIBuilder("SWEEP")
+        return builder.add_address("token").add_address("recipient").add_int("amountMin").build()
+
+    @staticmethod
+    def _build_pay_portion() -> _FunctionABI:
+        builder = _FunctionABIBuilder("PAY_PORTION")
+        return builder.add_address("token").add_address("recipient").add_int("bips").build()
diff --git a/uniswap_universal_router_decoder/_constants.py b/uniswap_universal_router_decoder/_constants.py
index 80d8347..bffd78b 100644
--- a/uniswap_universal_router_decoder/_constants.py
+++ b/uniswap_universal_router_decoder/_constants.py
@@ -15,7 +15,7 @@
 
 _execution_function_input_types = ["bytes", "bytes[]", "int"]
 _execution_function_selector = HexStr("0x3593564c")
-_router_abi = '[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"seaport","type":"address"},{"internalType":"address","name":"nftxZap","type":"address"},{"internalType":"address","name":"x2y2","type":"address"},{"internalType":"address","name":"foundation","type":"address"},{"internalType":"address","name":"sudoswap","type":"address"},{"internalType":"address","name":"nft20Zap","type":"address"},{"internalType":"address","name":"cryptopunks","type":"address"},{"internalType":"address","name":"looksRare","type":"address"},{"internalType":"address","name":"routerRewardsDistributor","type":"address"},{"internalType":"address","name":"looksRareRewardsDistributor","type":"address"},{"internalType":"address","name":"looksRareToken","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidOwnerERC1155","type":"error"},{"inputs":[],"name":"InvalidOwnerERC721","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"NoSlice","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"SliceOverflow","type":"error"},{"inputs":[],"name":"ToAddressOutOfBounds","type":"error"},{"inputs":[],"name":"ToAddressOverflow","type":"error"},{"inputs":[],"name":"ToUint24OutOfBounds","type":"error"},{"inputs":[],"name":"ToUint24Overflow","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnableToClaim","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsSent","type":"event"},{"inputs":[{"internalType":"bytes","name":"looksRareClaim","type":"bytes"}],"name":"collectRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]'  # noqa
+_router_abi = '[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"seaportV1_5","type":"address"},{"internalType":"address","name":"seaportV1_4","type":"address"},{"internalType":"address","name":"openseaConduit","type":"address"},{"internalType":"address","name":"nftxZap","type":"address"},{"internalType":"address","name":"x2y2","type":"address"},{"internalType":"address","name":"foundation","type":"address"},{"internalType":"address","name":"sudoswap","type":"address"},{"internalType":"address","name":"elementMarket","type":"address"},{"internalType":"address","name":"nft20Zap","type":"address"},{"internalType":"address","name":"cryptopunks","type":"address"},{"internalType":"address","name":"looksRareV2","type":"address"},{"internalType":"address","name":"routerRewardsDistributor","type":"address"},{"internalType":"address","name":"looksRareRewardsDistributor","type":"address"},{"internalType":"address","name":"looksRareToken","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"BuyPunkFailed","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidOwnerERC1155","type":"error"},{"inputs":[],"name":"InvalidOwnerERC721","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"InvalidSpender","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnableToClaim","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsSent","type":"event"},{"inputs":[{"internalType":"bytes","name":"looksRareClaim","type":"bytes"}],"name":"collectRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]'  # noqa
 
 _structured_data_permit: Dict[str, Any] = {
     'types': {
diff --git a/uniswap_universal_router_decoder/_decoder.py b/uniswap_universal_router_decoder/_decoder.py
index b2c2f95..a48c2f8 100644
--- a/uniswap_universal_router_decoder/_decoder.py
+++ b/uniswap_universal_router_decoder/_decoder.py
@@ -45,7 +45,7 @@ def function_input(self, input_data: Union[HexStr, HexBytes]) -> Tuple[ContractF
         command = decoded_input["commands"]
         command_input = decoded_input["inputs"]
         decoded_command_input = []
-        for i, b in enumerate(command[-6:]):
+        for i, b in enumerate(command[-7:]):
             # iterating over bytes produces integers
             try:
                 abi_mapping = self._abi_map[_RouterFunction(b)]
diff --git a/uniswap_universal_router_decoder/_encoder.py b/uniswap_universal_router_decoder/_encoder.py
index 3be62d2..0d5c283 100644
--- a/uniswap_universal_router_decoder/_encoder.py
+++ b/uniswap_universal_router_decoder/_encoder.py
@@ -214,8 +214,7 @@ def v2_swap_exact_in_from_balance(
             function_recipient: FunctionRecipient,
             amount_out_min: Wei,
             path: Sequence[ChecksumAddress],
-            custom_recipient: Optional[ChecksumAddress] = None,
-            payer_is_sender: bool = False) -> _ChainedFunctionBuilder:
+            custom_recipient: Optional[ChecksumAddress] = None) -> _ChainedFunctionBuilder:
         """
         Encode the call to the function V2_SWAP_EXACT_IN, using the router balance as amount_in,
         which swaps tokens on Uniswap V2.
@@ -226,7 +225,6 @@ def v2_swap_exact_in_from_balance(
         :param amount_out_min: The minimum accepted bought token (token_out)
         :param path: The V2 path: a list of 2 or 3 tokens where the first is token_in and the last is token_out
         :param custom_recipient: If function_recipient is CUSTOM, must be the actual recipient, otherwise None.
-        :param payer_is_sender: Always False. Will be removed in next version
         :return: The chain link corresponding to this function call.
         """
         return self.v2_swap_exact_in(
@@ -235,7 +233,7 @@ def v2_swap_exact_in_from_balance(
             amount_out_min,
             path,
             custom_recipient,
-            payer_is_sender,
+            False,
         )
 
     def _encode_v2_swap_exact_out_sub_contract(
@@ -341,8 +339,7 @@ def v3_swap_exact_in_from_balance(
             function_recipient: FunctionRecipient,
             amount_out_min: Wei,
             path: Sequence[Union[int, ChecksumAddress]],
-            custom_recipient: Optional[ChecksumAddress] = None,
-            payer_is_sender: bool = False) -> _ChainedFunctionBuilder:
+            custom_recipient: Optional[ChecksumAddress] = None) -> _ChainedFunctionBuilder:
         """
         Encode the call to the function V3_SWAP_EXACT_IN, using the router balance as amount_in,
         which swaps tokens on Uniswap V3.
@@ -354,7 +351,6 @@ def v3_swap_exact_in_from_balance(
         :param path: The V3 path: a list of tokens where the first is the token_in, the last one is the token_out, and
         with the pool fee between each token in basis points (ex: 3000 for 0.3%)
         :param custom_recipient: If function_recipient is CUSTOM, must be the actual recipient, otherwise None.
-        :param payer_is_sender: Always False. Will be removed in next version
         :return: The chain link corresponding to this function call.
         """
         return self.v3_swap_exact_in(
@@ -363,7 +359,7 @@ def v3_swap_exact_in_from_balance(
             amount_out_min,
             path,
             custom_recipient,
-            payer_is_sender,
+            False,
         )
 
     def _encode_v3_swap_exact_out_sub_contract(
@@ -454,6 +450,82 @@ def permit2_permit(
         )
         return self
 
+    def _encode_sweep_sub_contract(self, token: ChecksumAddress, recipient: ChecksumAddress, amount_min: Wei) -> HexStr:
+        abi_mapping = self._abi_map[_RouterFunction.SWEEP]
+        sub_contract = self._w3.eth.contract(abi=abi_mapping.fct_abi.get_full_abi())
+        contract_function: ContractFunction = sub_contract.functions.SWEEP(token, recipient, amount_min)
+        return remove_0x_prefix(encode_abi(self._w3, contract_function.abi, [token, recipient, amount_min]))
+
+    def sweep(
+            self,
+            function_recipient: FunctionRecipient,
+            token_address: ChecksumAddress,
+            amount_min: Wei,
+            custom_recipient: Optional[ChecksumAddress] = None) -> _ChainedFunctionBuilder:
+        """
+        Encode the call to the function SWEEP which sweeps all of the router's ERC20 or ETH to an address
+
+        :param function_recipient: A FunctionRecipient which defines the recipient of this function output.
+        :param token_address: The address of the token to sweep or "0x0000000000000000000000000000000000000000" for ETH.
+        :param amount_min: The minimum desired amount
+        :param custom_recipient: If function_recipient is CUSTOM, must be the actual recipient, otherwise None.
+        :return: The chain link corresponding to this function call.
+        """
+        recipient = self._get_recipient(function_recipient, custom_recipient)
+        self.commands.append(_RouterFunction.SWEEP)
+        self.arguments.append(
+            Web3.to_bytes(
+                hexstr=self._encode_sweep_sub_contract(
+                    token_address,
+                    recipient,
+                    amount_min,
+                )
+            )
+        )
+        return self
+
+    def _encode_pay_portion_sub_contract(self, token: ChecksumAddress, recipient: ChecksumAddress, bips: int) -> HexStr:
+        abi_mapping = self._abi_map[_RouterFunction.PAY_PORTION]
+        sub_contract = self._w3.eth.contract(abi=abi_mapping.fct_abi.get_full_abi())
+        contract_function: ContractFunction = sub_contract.functions.PAY_PORTION(token, recipient, bips)
+        return remove_0x_prefix(encode_abi(self._w3, contract_function.abi, [token, recipient, bips]))
+
+    def pay_portion(
+            self,
+            function_recipient: FunctionRecipient,
+            token_address: ChecksumAddress,
+            bips: int,
+            custom_recipient: Optional[ChecksumAddress] = None) -> _ChainedFunctionBuilder:
+        """
+        Encode the call to the function PAY_PORTION which transfers a part of the router's ERC20 or ETH to an address.
+        Transferred amount = balance * bips / 10_000
+
+        :param function_recipient: A FunctionRecipient which defines the recipient of this function output.
+        :param token_address: The address of token to pay or "0x0000000000000000000000000000000000000000" for ETH.
+        :param bips: integer between 0 and 10_000
+        :param custom_recipient: If function_recipient is CUSTOM, must be the actual recipient, otherwise None.
+        :return: The chain link corresponding to this function call.
+        """
+        if (
+            bips < 0
+            or bips > 10_000
+            or not isinstance(bips, int)
+        ):
+            raise ValueError(f"Invalid argument: bips must be an int between 0 and 10_000. Received {bips}")
+
+        recipient = self._get_recipient(function_recipient, custom_recipient)
+        self.commands.append(_RouterFunction.PAY_PORTION)
+        self.arguments.append(
+            Web3.to_bytes(
+                hexstr=self._encode_pay_portion_sub_contract(
+                    token_address,
+                    recipient,
+                    bips,
+                )
+            )
+        )
+        return self
+
     def build(self, deadline: Optional[int] = None) -> HexStr:
         """
         Build the encoded input for all the chained commands, ready to be sent to the UR
diff --git a/uniswap_universal_router_decoder/_enums.py b/uniswap_universal_router_decoder/_enums.py
index be15b92..4d7abc0 100644
--- a/uniswap_universal_router_decoder/_enums.py
+++ b/uniswap_universal_router_decoder/_enums.py
@@ -17,6 +17,8 @@ class _RouterFunction(Enum):
     # https://docs.uniswap.org/contracts/universal-router/technical-reference#command
     V3_SWAP_EXACT_IN = 0
     V3_SWAP_EXACT_OUT = 1
+    SWEEP = 4
+    PAY_PORTION = 6
     V2_SWAP_EXACT_IN = 8
     V2_SWAP_EXACT_OUT = 9
     PERMIT2_PERMIT = 10
@@ -25,6 +27,13 @@ class _RouterFunction(Enum):
 
 
 class FunctionRecipient(Enum):
+    """
+    SENDER: When the function recipient is the sender
+
+    ROUTER: When the function recipient is the router
+
+    CUSTOM: When the function recipient is neither the trx sender nor the router
+    """
     SENDER = "recipient is transaction sender"
     ROUTER = "recipient is universal router"
     CUSTOM = "recipient is custom"


Test patch:
diff --git a/integration_tests/main_2.py b/integration_tests/main_2.py
index 985af72..0b8f57b 100644
--- a/integration_tests/main_2.py
+++ b/integration_tests/main_2.py
@@ -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())
     )
@@ -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())
     )
diff --git a/integration_tests/main_3.py b/integration_tests/main_3.py
index 4b7ae43..9d29ade 100644
--- a/integration_tests/main_3.py
+++ b/integration_tests/main_3.py
@@ -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())
     )
diff --git a/integration_tests/main_3_bsc.py b/integration_tests/main_3_bsc.py
index 6d5506d..1f8fae5 100644
--- a/integration_tests/main_3_bsc.py
+++ b/integration_tests/main_3_bsc.py
@@ -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())
     )
diff --git a/tests/test_abi_builder.py b/tests/test_abi_builder.py
index cd79a58..7261637 100644
--- a/tests/test_abi_builder.py
+++ b/tests/test_abi_builder.py
@@ -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):
diff --git a/tests/test_decoder.py b/tests/test_decoder.py
index ee3a831..aebee50 100644
--- a/tests/test_decoder.py
+++ b/tests/test_decoder.py
@@ -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(
@@ -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):
diff --git a/tests/test_encoder.py b/tests/test_encoder.py
index a005a66..ee0d683 100644
--- a/tests/test_encoder.py
+++ b/tests/test_encoder.py
@@ -65,7 +65,6 @@ def test_chain_v2_swap_exact_in_from_balance(codec):
             Web3.to_checksum_address("0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"),
             Web3.to_checksum_address("0x326C977E6efc84E512bB9C30f76E30c160eD06FB"),
         ],
-        payer_is_sender=False,
     ).build(1676919287)
     assert encoded_input == HexStr("0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063f3c1f70000000000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000326c977e6efc84e512bb9c30f76e30c160ed06fb")  # noqa E501
 
@@ -148,7 +147,6 @@ def test_chain_v3_swap_exact_in_from_balance(codec):
             500,
             Web3.to_checksum_address("0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"),
         ],
-        payer_is_sender=False,
     ).build(1677080627)
     assert encoded_input == HexStr("0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063f63833000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000383747ae00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f42260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000000000000000000000000000000000000000")  # noqa E501
 
@@ -240,3 +238,55 @@ def test_chain_v2_swap_exact_in_with_permit(codec):
     )
 
     assert encoded_input == HexStr('0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063fa0e9900000000000000000000000000000000000000000000000000000000000000020a080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001600000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000642194910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b0000000000000000000000000000000000000000000000000000000063fa0e9900000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041c18b2a09e7d03f38102824a401a765fe05a40753a2188520250e7ffb825f41960dd8703c82a9683c0a7a8eafb2b930aa29b3edc018cad7afc6e29781420a8ec51b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2')  # noqa E501
+
+
+def test_chain_v2_swap_exact_in_and_sweep_and_pay_portion(codec):
+    # https://etherscan.io/tx/0x688f928224146be1bb99556878b72098592201d5a9024fa5e053dbb8b7f02959
+    encoded_input = (
+        codec
+        .encode
+        .chain()
+        .v2_swap_exact_in(
+            FunctionRecipient.ROUTER,
+            Wei(2000000000000000000000000),
+            Wei(986619877196212818407780752),
+            [
+                '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE',
+                '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
+                '0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C',
+            ],
+            payer_is_sender=True,
+        )
+        .pay_portion(
+            FunctionRecipient.CUSTOM,
+            Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'),
+            100,
+            Web3.to_checksum_address('0x17CC6042605381c158D2adab487434Bde79Aa61C'),
+        )
+        .sweep(
+            FunctionRecipient.SENDER,
+            Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'),
+            Wei(986619877196212818407780752),
+        )
+        .build(1698245843)
+    )
+
+    assert encoded_input == HexStr('0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000065392cd3000000000000000000000000000000000000000000000000000000000000000308060400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000001a784379d99db42000000000000000000000000000000000000000000000003301ce2b6b374a8a606f19000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000095ad61b0a150d79219dcf64e1e6cc01f0b64c4ce000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000001ce270557c1f68cfb577b856766310bf8b47fd9c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000001ce270557c1f68cfb577b856766310bf8b47fd9c00000000000000000000000017cc6042605381c158d2adab487434bde79aa61c000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000600000000000000000000000001ce270557c1f68cfb577b856766310bf8b47fd9c0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000003301ce2b6b374a8a606f190')  # noqa
+
+
+@pytest.mark.parametrize(
+    "function_recipient, token_address, bips, custom_recipient, expected_exception",
+    (
+        (FunctionRecipient.SENDER, Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'), 100, None, None),  # noqa
+        (FunctionRecipient.SENDER, Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'), 100.01, None, ValueError),  # noqa
+        (FunctionRecipient.SENDER, Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'), 10_001, None, ValueError),  # noqa
+        (FunctionRecipient.SENDER, Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'), -1, None, ValueError),  # noqa
+        (FunctionRecipient.CUSTOM, Web3.to_checksum_address('0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C'), 100, None, ValueError),  # noqa
+    )
+)
+def test_pay_portion_argument_validity(function_recipient, token_address, bips, custom_recipient, expected_exception, codec):  # noqa
+    if expected_exception:
+        with pytest.raises(ValueError):
+            codec.encode.chain().pay_portion(function_recipient, token_address, bips, custom_recipient).build(1698245843)  # noqa
+    else:
+        _ = codec.encode.chain().pay_portion(function_recipient, token_address, bips, custom_recipient).build(1698245843)  # noqa