
Uniswap Universal Router Decoder
io.github.ElnarilPublished by GitHub account Elnarilv4.0.0Updated Oct 29, 2026
Decode and encode Uniswap Universal Router calldata (V2, V3, V4). Read-only: never signs or sends.
The publisher's own server: 370.ai — AI Gateway: 100+ Chat Models, Image & Speech Generation
Overview
Local MCP server that decodes and encodes Uniswap Universal Router calldata for V2, V3 and V4 swaps, read-only and never signing or sending.
- What you can do
- Decode Universal Router transaction input into commands, parameters, deadline and trailing data
- Decode a full transaction by hash, fetching it from an RPC endpoint
- Encode and decode Uniswap V3 swap paths, and build V4 pool keys, path keys and pool ids
- Rebuild calldata from a decoded call, so a route can be decoded, edited and re-encoded
- Encode chained router commands such as swaps, wrap ETH, Permit2 permit, pay portion, sweep and transfer
- When to use it
- Useful when you need to inspect what a Uniswap Universal Router transaction actually does, or when you want an assistant to construct or adjust router calldata for a swap, liquidity or Permit2 flow before you sign it yourself.
- Requirements
- Runs locally as a Python package (installed with uvx or pip). Decoding a whole transaction by hash needs an Ethereum RPC endpoint, supplied through the RPC_URL environment variable; decoding raw calldata and encoding do not.
Installation
In SourceWeft
- Open Uniswap Universal Router Decoder in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
Uniswap Universal Router Decoder & Encoder
Many thanks to everyone who has ☕️ offered some coffees! ☕️ or ⭐ starred this project! ⭐
It is greatly appreciated! :)
Project Information
[PyPI - Python Version] [GitHub release (latest by date)] [PyPi Repository] [GitHub] [Uniswap Universal Router Decoder MCP on mcprush]
Code Quality
[Tests & Lint] [CodeQL] [Test Coverage] [Imports: isort] [Type Checker: basedpyright] [Linter: flake8]
Release Notes
See the release notes page
v4.0.0 - breaking changes
- Update COMMAND_TYPE_MASK to 0x7f to match UR
- Add support for the PAY_PORTION_FULL_PRECISION command (0x07)
- Add support for the BALANCE_CHECK_ERC20 command (0x0e)
- Add support for the V3_POSITION_MANAGER_PERMIT command (0x11)
- Add support for the V3_POSITION_MANAGER_CALL command (0x12)
- Add support for the EXECUTE_SUB_PLAN command (0x21)
- Add
revert_on_failto the encoder methods for the commands whose failure the UR can tolerate - Capture the bytes some callers append after the UR arguments as
trailing_data, and re-append them when rebuilding the calldata, so a decoded call can be rebuilt byte-for-byte - Make the chained function builders public and expose the
encoder&decodermodules - Reduce CI time
- Add a
publish.ymlGitHub Actions workflow that automatically publishes the package to PyPI and the MCP Registry with OIDC Trusted Publishing - Agent-friendly changes:
- Add
Encoder.function_input()/AsyncEncoder.function_input()to rebuild the calldata from a decoded call - Add
decoded_input_to_dict()andtransaction_to_dict()to render the decoder output as JSON-friendly dicts - Add the
ur-decodeandur-encodecommand line tools, includingur-encode v3-pathand itsencode_v3_pathMCP counterpart - Add the
ur-mcpMCP server (optionalmcpextra) so AI agents can drive the codec, includingencode_calldatato rebuild the calldata from a decoded call - Annotate the MCP tools as read-only so a host can auto-approve them, and give them human-readable titles
- The command descriptions (
ur-encode list/describeand thelist_commands/describe_commandMCP tools) now describe the decoded call format and its ABI parameters, instead of the encoder methods - Add the MCP Registry metadata (
server.json) to make theur-mcpserver discoverable - Add
AGENTS.mdandllms.txtfor AI agents, plus amcpPyPI keyword - Add
tests/test_version_consistency.pyto keep the packaging version,server.jsonand the READMEmcp-name:marker in sync
- Add
Overview and Points of Attention
The object of this library is to decode & encode transactions sent to the Uniswap Universal Router (UR) on Ethereum Mainnet, though it would work on any compatible chain where the UR is deployed. See Version Mapping below. This Python SDK is based on, and intended to be used with web3.py
The target audience is Python developers who are familiar with the Ethereum blockchain concepts and web3.py, and how DEXes work.
The same codec is also available without Python, through the ur-decode / ur-encode command line tools and the optional
ur-mcp MCP server (see "Command line interface" and "MCP server" below): both print JSON, so shell scripts and AI
agents can drive it too.
⚠ Before using this library, ensure you are familiar with general blockchain concepts. The Python API also expects web3.py knowledge in particular.
⚠ Not all UR functions supported by this SDK are available through the CLI or MCP tools:
run ur-encode list and look for the "rebuildable" flag.
List of supported Uniswap Universal Router Functions
All the ✅ commands can be decoded and encoded. V4_SWAP, V4_POSITION_MANAGER_CALL and EXECUTE_SUB_PLAN can also
be decoded, but they cannot be rebuilt from a decoded call (see "How to rebuild the calldata from a decoded call").
Version Mapping
Here is the mapping between the Universal Router versions and the SDK ones
Installation
A good practice is to use Python virtual environments, here is a tutorial.
The library can be pip installed from pypi.org as usual:
To install it from source:
or for contributors:
Usage
The library exposes 2 classes, RouterCodec and AsyncRouterCodec with several public methods
that can be used to decode or encode UR data and transactions.
Both RouterCodec and AsyncRouterCodec are used the same way but either synchronously
or asynchronously for the relevant parts, ie when doing rpc calls.
How to instantiate the router codec
1/ When no need for rpc calls
2/ Using the URL of an rpc endpoint
Sync code:
Async code:
3/ Using a Web3/AsyncWeb3 instance
Sync code:
Async code:
How to decode a Uniswap Universal Router transaction input
To decode a transaction input, use the decode.function_input() method as follows:
Example of decoded input returned by decode.function_input():
decode.function_input() reports the bytes some callers append after the UR arguments as trailing_data (see the
example above). Pass find_trailing_data=False to ignore them.
Tip: You can format the decoded input like above with this Gedit plugin ;)
How to decode a Uniswap Universal Router transaction
It's also possible to decode the whole transaction, given its hash
and providing the codec has been built with either a valid Web3 instance or the link to a rpc endpoint.
The decoder will get the transaction from the blockchain and decode it, along with its input data:
How to encode/decode a Uniswap V3 swap path
RouterCodec exposes two mirror methods: encode.v3_path() builds a Uniswap V3 path from the ordered tokens and the
pool fees between them, and decode.v3_path() reads one back.
decode.v3_path() returns a tuple, starting with the "in-token" and ending with the "out-token", with the pool fees
between each pair. Both methods use that same order: V3_SWAP_EXACT_OUT reverses the path when encoding, and the
decoder reverses it back.
How to get the decoded data as JSON-friendly dicts
The decoder returns rich Python objects (web3 contract functions, raw bytes, ...) that are not directly serialisable. Two helpers render that output as plain dicts/lists/strings, ready to be printed as JSON or sent to another system:
decoded_input_to_dict()takes the output ofdecode.function_input()transaction_to_dict()takes the output ofdecode.transaction()
Bytes are rendered as "0x"-prefixed hex strings, and each decoded command becomes a dict with a command name,
its params, and a revert_on_fail flag:
How to encode
The Uniswap 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 returns the full encoded data to be included in the transaction.
Below some examples of encoded data for one function and one example for 2 functions.
Starting with v3.0.0, it is possible to start the chaining with encode() instead of encode.chain(), making the code a bit more concise.
Default values for deadlines and expirations can be computed with the static methods get_default_deadline() and get_default_expiration() respectively.
These 2 functions accept a custom duration in seconds as argument.
revert_on_fail can be passed to the methods whose command the UR can tolerate failing: permit2_permit(),
permit2_permit_batch(), balance_check_erc20(), v3_position_manager_permit(), v3_decrease_liquidity(),
v3_collect(), v3_burn(), v4_initialize_pool(), build_v4_posm_call() and execute_sub_plan(). It defaults to
True; set it to False to let the transaction continue if that command fails. The other methods don't accept it,
because the UR ignores that flag for their command.
How to rebuild the calldata from a decoded call
As an alternative to chaining the encoder methods, encode.function_input() builds the calldata from the decoded form
of an execute call. It is the mirror of decode.function_input(): where the decoder turns a function input into
commands, this turns the decoded commands back into a function input, so a route can be decoded, edited, and re-encoded.
The expected shape is the one produced by decoded_input_to_dict() (and by the ur-decode CLI and the MCP server):
its params.inputs hold the commands, using the names and parameters the decoder reports (the Universal Router ABI
names).
The function key is optional and defaults to execute, and the commands bytes are ignored: the command list is
rebuilt from the inputs entries. The deadline key is optional too; when it is absent, the plain
execute(bytes,bytes[]) form is used instead of execute(bytes,bytes[],uint256).
Some callers append a tag after the ABI-encoded arguments, which the UR ignores. The decoder reports it as
params.trailing_data, and encode.function_input() re-appends it, so a decoded call can be rebuilt byte-for-byte.
The chained builders take the same value as an optional trailing_data argument of build(), after deadline.
Commands the decoder expands into structures (V4_SWAP, V4_POSITION_MANAGER_CALL, EXECUTE_SUB_PLAN) cannot be
rebuilt this way and raise a ValueError.
revert_on_fail is honoured only for the commands whose failure the UR can tolerate (PERMIT2_PERMIT,
PERMIT2_PERMIT_BATCH, BALANCE_CHECK_ERC20, V3_POSITION_MANAGER_PERMIT, V3_POSITION_MANAGER_CALL,
V4_INITIALIZE_POOL, V4_POSITION_MANAGER_CALL, EXECUTE_SUB_PLAN). For every other command the UR ignores the
flag, so it is always cleared. The same applies to the encoder methods: those commands take a revert_on_fail
argument (default True), the others do not.
AsyncRouterCodec exposes the same method (encode.function_input()); it is synchronous, so no await is needed.
How to encode a call to the Uniswap Universal Router function WRAP_ETH
This function can be used to convert eth to weth using the UR.
How to encode a call to the Uniswap V2 function V2_SWAP_EXACT_IN
This function can be used to swap tokens on a V2 pool. Correct allowances must have been set before sending such transaction.
For more details, see this tutorial
How to encode a call to the Uniswap V2 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.
How to encode a call to the Uniswap V3 function V3_SWAP_EXACT_IN
This function can be used to swap tokens on a V3 pool. Correct allowances must have been set before sending such transaction.
How to encode a call to the Uniswap V3 function V3_SWAP_EXACT_OUT
This function can be used to swap tokens on a V3 pool. Correct allowances must have been set before sending such transaction.
How to encode a call to the Uniswap Universal Router function PERMIT2_PERMIT
This function is used to give an allowance to the universal router via the Permit2 contract (0x000000000022D473030F116dDEE9F6B43aC78BA3).
It is also necessary to approve the Permit2 contract using the token approve function.
See this tutorial
After that, you can swap tokens using the Uniswap universal router.
How to get the current permit2 allowance, expiration and nonce
You can get the nonce you need to build the permit2 signable message like this:
How to chain a call to PERMIT2_PERMIT and V2_SWAP_EXACT_IN in the same transaction
Don't forget to give a token allowance to the Permit2 contract as well.
Uniswap V4 Functions
How to build a Uniswap V4 pool key
PoolKey is used to identify a pool.
How to build a Uniswap V4 path key
PathKey is used to identify pools in a multi-hop swaps.
How to encode a Uniswap V4 pool id
Pool ids are used by V4 contracts to identify pools. For example, you'll need it to request the StateView contract.
How to initialize a Uniswap V4 pool with the Universal Router function V4_INITIALIZE_POOL
How to mint a Uniswap V4 position with the Universal Router function V4_POSITION_MANAGER_CALL
Example where a position on a ETH/TOKEN pool is minted between tick_0 and tick_1
How to perform a Uniswap V4 swap with the Universal Router function V4_SWAP
Example using the V4 SWAP_EXACT_IN_SINGLE function to swap ETH for a token
Example using the V4 SWAP_EXACT_IN function to swap ETH for a token through 2 pools
path: eth -> token a -> token b
Other chainable functions
(See integration tests for full examples)
PAY_PORTION
Example where a recipient is paid 1% of the USDC amount:
PAY_PORTION_FULL_PRECISION
Same as PAY_PORTION but with full precision, where 10**18 represents 100%. Example where a recipient is paid 50% of the USDC amount:
SWEEP
Example where the sender gets back all remaining USDC:
TRANSFER
Example where a USDC amount is sent to a recipient:
BALANCE_CHECK_ERC20
Reverts if the owner's balance of the given ERC20 token is lower than the minimum amount. Example where the router must hold at least 1 USDC:
EXECUTE_SUB_PLAN
Executes a nested plan of commands. The Universal Router calls itself to run the sub-plan, so its commands
are executed in the same transaction and share the same context (recipients, balances, ...).
The sub-plan is built with encode.chain() just like the main plan.
Example where a sub-plan transferring USDC is nested in the main plan:
V3_POSITION_MANAGER_PERMIT
Approves a spender for a specific V3 position (NFT) using an EIP-712 signature instead of a transaction.
The signable message is built with create_v3_position_manager_permit_signable_message(), then signed.
Example where the spender is approved for the position token_id:
V3_POSITION_MANAGER_CALL
Calls one of the 3 V3 position manager functions the UR allows: decreaseLiquidity, collect or burn.
Only one function can be called per V3_POSITION_MANAGER_CALL command: the UR forwards the input as a single
call and rejects anything else (including multicall). To run several of them, chain several commands in the
same transaction (see the integration test).
The UR must be approved for the position first (see V3_POSITION_MANAGER_PERMIT).
decreaseLiquidityremoves liquidity from the position. The freed tokens are NOT sent out: they are recorded astokensOwed0/tokensOwed1on the position, together with ALL the fees earned since the last update (fees are not prorated, even if only part of the liquidity is removed).collectsendsmin(amountMax, tokensOwed)to the recipient.tokensOwedholds both the principal freed bydecreaseLiquidityand the fees, socollectalone harvests the fees without touching the liquidity.burndeletes the position (NFT). It requires the position to be empty (liquidity == 0andtokensOwed0 == tokensOwed1 == 0), hence the usualdecreaseLiquidity->collect->burnorder.
Note: increaseLiquidity and mint are not reachable through the UR (only the 3 functions above are allowed),
they must be called directly on the V3 position manager.
Example where the liquidity of a V3 position is decreased:
Example where the tokens owed to a V3 position are collected:
Example where an empty V3 position is burned:
PERMIT2_TRANSFER_FROM
To use this function, the Universal Router needs to be PERMIT2'ed.
Example where 1 WETH is transferred from the caller address to the v4 position manager
PERMIT2_PERMIT_BATCH & PERMIT2_TRANSFER_FROM_BATCH
Check the corresponding integration tests for an example of how to use these functions.
How to build directly a transaction to the Uniswap Universal Router
The SDK provides a handy method to build very easily the full transaction in addition to the input data. It can compute most of the transaction parameters (if the codec has been instantiated with a valid w3 or rpc url) or you can provide them.
See the corresponding integration tests for an async example
Example where a swap is encoded and a transaction is built automatically:
And that's it! You can now sign and send this transaction. A few other important parameters:
value: the quantity of ETH in wei you send to the Universal Router (for example when you wrap them before a swap)trx_speed: an enum which influences the transaction rank in the block. Values are:TransactionSpeed.SLOWTransactionSpeed.AVERAGETransactionSpeed.FAST(default)TransactionSpeed.FASTER
max_fee_per_gas_limit: if the computedmax_fee_per_gasis greater thanmax_fee_per_gas_limitaValueErrorwill be raised to allow you to stay in control. Default is 100 gwei.
How to use the trx_speed parameter:
Command line interface
The package installs two commands that print JSON to stdout, handy for shell scripts or to let an agent drive the codec:
ur-encode: describe the decoded call format and the commands, rebuild calldata from a decoded call, or encode a V3 pathur-decode: decode a transaction, raw calldata, a custom error or a V3 path
A plan file holds a decoded execute call, exactly as ur-decode calldata prints it:
MCP server
The optional mcp extra installs ur-mcp, a side-effect free Model Context
Protocol server that exposes the codec to AI agents over stdio. Its tools only
inspect or derive Universal Router data: they never sign or broadcast a transaction.
It provides eight tools:
list_commands/describe_command: discover the decoded call format and the commandsdecode_transaction: fetch and decode a transaction sent to the UR (needs an RPC endpoint)decode_calldata,encode_calldata,decode_error,decode_v3_path,encode_v3_path: decode/encode data without an RPC endpoint
encode_calldata is the mirror of decode_calldata: it rebuilds the calldata from a decoded call, so an agent can
decode a route, edit it and re-encode it (the MCP counterpart of ur-encode build).
The tools are annotated as read-only (readOnlyHint), and only decode_transaction as open-world (openWorldHint),
so a host can auto-approve them instead of prompting on every call. Each tool also carries a human-readable title
(title) for the host UI.
decode_transaction uses the rpc_url tool argument, or the RPC_URL / ETH_RPC_URL environment variables, so a
host can be pointed at any chain by configuring the server environment.
To wire it into an MCP host (Zed, Claude Desktop, ...):
Utility functions
How to compute the gas fees
The SDK provides handy methods to compute the current "priority fee" and "max fee per gas":
Sync code:
Async code:
Ticks, sqrtPriceX96 and liquidity
These functions can be useful to estimate the arguments needed to mint positions.
⚠️ The implementation of all tick, sqrtPriceX96, and liquidity related functions differs from the contracts: if you want the exact same results/numbers, do not use them.
They can be imported from uniswap_universal_router_decoder.utils.
For examples, check the integration tests for Uniswap V4.
Tutorials and Recipes on the Uniswap Universal Router:
See the SDK Wiki.
News and Q&A:
See the Discussions and 𝕏
Source: README.md at commit dc4a725
Tools
0Version history
1- v4.0.0LatestOct 29, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
Other Router integrations
370.ai — AI Gateway: 100+ Chat Models, Image & Speech Generation
ai.routerOfficial publisher
Remote AI gateway MCP server that lets an assistant call 100+ chat models, generate images, and convert text to speech through one 370.ai API key.

AI Media Router
Published by GitHub account jutsuhq
Local MCP server that generates images and videos through 40+ providers using your own API keys, with price comparison and a searchable local asset library.

Bike Router
Published by GitHub account JonasHeinickeBio
MCP bridge to a self-hosted pyBikeRouter API that plans OSM-based bike routes with weather, sights and GeoJSON/GPX exports.

x402 Router
Published by GitHub account camerxnduff
Read-only MCP server that searches a probed registry of live x402 pay-per-call endpoints and routes an agent to the best-scored one.

GenPark Consistent Hash Ring Router
Published by GitHub account alphaparkinc
A local MCP server providing a consistent hashing ring with virtual nodes, for routing distributed LLM caches and partitioning agents.