## Understanding the Uniswap Universal Router The Uniswap Universal Router is a powerful, unified smart contract designed to streamline token swaps across the entire Uniswap ecosystem. It acts as a single entry point for executing trades that can leverage Uniswap v2, v3, and the upcoming v4 protocols, all within a single, composable transaction. This architecture provides developers with maximum flexibility and gas efficiency when routing complex trades. This lesson offers a technical deep dive into its code architecture and execution flow. It is important to note that the Universal Router contract is both unowned and non-upgradeable, ensuring its behavior remains predictable and decentralized. ## Code Architecture and Execution Flow To understand how the Universal Router works, we will explore its core smart contracts, primarily focusing on the command-based system that drives its functionality. The source code is publicly available in the `Uniswap/universal-router` GitHub repository. ### The Entry Point: `UniversalRouter.sol` All interactions with the Universal Router begin by calling a single function: `execute`. This function is the cornerstone of the router's flexible design, processing a dynamic list of commands and their corresponding data. The `execute` function accepts two key arguments: * `bytes calldata commands`: A byte string where each individual byte represents a specific action to be performed, such as a V3 swap, wrapping ETH, or interacting with Permit2. * `bytes[] calldata inputs`: An array of byte strings. Each element in this array contains the ABI-encoded parameters required for the corresponding command in the `commands` string. The function logic iterates through each command, dispatches it for execution, and handles the results. ```solidity /// @inheritdoc Dispatcher function execute(bytes calldata commands, bytes[] calldata inputs) public payable override { // ... // loop through all given commands, execute them and pass along outputs for (uint256 commandIndex = 0; commandIndex < numCommands; commandIndex++) { bytes1 command = commands[commandIndex]; bytes calldata input = inputs[commandIndex]; (success, output) = dispatch(command, input); if (!success && successRequired(command)) { revert ExecutionFailed(commandIndex: commandIndex, message: output); } } } ``` In this loop, for each command byte, the contract retrieves the associated input data and passes both to an internal `dispatch` function. If a required command fails, the entire transaction reverts. ### The Command Handler: `Dispatcher.sol` The `dispatch` function, located in the `Dispatcher.sol` contract, serves as the central logic hub of the router. Its sole responsibility is to interpret the command byte and route the execution to the appropriate internal function. This is implemented as a large series of `if` / `else if` statements. Each condition checks the `command` against a predefined constant. If a match is found, the `dispatch` function decodes the `inputs` byte string into the specific parameters needed for that action and calls the relevant implementation function. ```solidity function dispatch(bytes1 commandType, bytes calldata inputs) internal returns (bool success, bytes memory output) { uint256 command = uint8(commandType & Commands.COMMAND_TYPE_MASK); success = true; // ... many if/else if statements for different commands // Example for a V3 Swap if (command == Commands.V3_SWAP_EXACT_IN) { // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool)) // ... assembly to decode inputs ... address payer = payerIsUser ? msgSender() : address(this); v3SwapExactInput(map[recipient], amountIn, amountOutMin, path, payer); } // Example for a V4 Swap else if (command == Commands.V4_SWAP) { // pass the calldata provided to V4SwapRouter._executeActions _executeActions(inputs); } // ... more commands } ``` As shown in the example, the logic for a `V3_SWAP_EXACT_IN` involves decoding five distinct parameters from the `inputs` and calling `v3SwapExactInput`. In contrast, the `V4_SWAP` command simply passes the raw `inputs` data to a more specialized function, `_executeActions`, which we will explore later. ### Defining Commands: `Commands.sol` To ensure code readability and prevent errors from using raw hexadecimal or integer values, all available command identifiers are defined as constants in the `Commands.sol` library. This file contains a comprehensive list of all actions the router can perform, such as `V3_SWAP_EXACT_IN`, `V2_SWAP_EXACT_IN`, `WRAP_ETH`, `PERMIT2_PERMIT`, and `V4_SWAP`. ### How to Determine Input Parameters For a developer to correctly format the `inputs` array, they need to know the exact parameters and encoding scheme for each command. There are two primary ways to find this information: 1. **Read the Source Code:** By inspecting the `if` block for a specific command within `Dispatcher.sol`, you can see precisely how the contract decodes the `inputs` data. The comments and `abi.decode` or assembly logic reveal the expected data types and order. 2. **Consult the Official Documentation:** The Uniswap Docs provide a "Technical Reference" for the Universal Router. This resource explicitly lists every command and its required input parameters. For example, the documentation specifies that the `inputs` for a `V3_SWAP_EXACT_IN` command must be the ABI-encoded `bytes` of the following parameters in order: * `address recipient` * `uint256 amountIn` * `uint256 amountOutMin` * `bytes path` * `bool payerIsUser` ## The Execution Flow for a Uniswap v4 Swap Executing a Uniswap v4 swap through the Universal Router showcases the contract's sophisticated, multi-layered architecture, which relies heavily on contract inheritance and callbacks. Here is the step-by-step flow: 1. A user calls `execute` on `UniversalRouter.sol`, providing `Commands.V4_SWAP` as a command. 2. The `execute` function calls `dispatch` in the `Dispatcher.sol` logic. 3. The `if (command == Commands.V4_SWAP)` condition is met, which calls `_executeActions(inputs)`. 4. The `_executeActions` function is not defined in `Dispatcher` but is inherited. The inheritance chain is: `Dispatcher` inherits from `V4SwapRouter`, which inherits from `V4Router`, which inherits from `BaseActionsRouter`. 5. The function is found in `BaseActionsRouter.sol`. It calls `poolManager.unlock(unlockData)`, which is the entry point into the core Uniswap v4 protocol. 6. The Uniswap v4 `PoolManager` contract, as a security measure, makes a callback to the contract that called it (the Universal Router). 7. The router's `unlockCallback` function, also in `BaseActionsRouter.sol`, is triggered by the `PoolManager`. This callback function decodes the actions to be performed. 8. Inside the callback, `_executeActionsWithoutUnlock` is called. This function loops through each requested action (e.g., swap, settle). 9. For each action, it calls `_handleAction`, which is defined further down the inheritance chain in `V4Router.sol`. 10. Finally, `_handleAction` contains the specific logic to execute V4 actions like `Actions.SWAP_EXACT_IN` by calling the appropriate functions on the `PoolManager`. ## Summary The Uniswap Universal Router provides a unified and extensible interface for the entire Uniswap protocol suite. Its architecture is built on a simple yet powerful principle: an `execute` function that processes a list of commands. These commands are parsed by a central `Dispatcher`, which routes them to the correct implementation logic. For complex integrations like Uniswap v4, the router uses a layered system of inherited contracts and callbacks to interact securely and efficiently with the core protocol.
The Uniswap Universal Router is a powerful, unified smart contract designed to streamline token swaps across the entire Uniswap ecosystem. It acts as a single entry point for executing trades that can leverage Uniswap v2, v3, and the upcoming v4 protocols, all within a single, composable transaction. This architecture provides developers with maximum flexibility and gas efficiency when routing complex trades. This lesson offers a technical deep dive into its code architecture and execution flow.
It is important to note that the Universal Router contract is both unowned and non-upgradeable, ensuring its behavior remains predictable and decentralized.
To understand how the Universal Router works, we will explore its core smart contracts, primarily focusing on the command-based system that drives its functionality. The source code is publicly available in the Uniswap/universal-router GitHub repository.
UniversalRouter.solAll interactions with the Universal Router begin by calling a single function: execute. This function is the cornerstone of the router's flexible design, processing a dynamic list of commands and their corresponding data.
The execute function accepts two key arguments:
bytes calldata commands: A byte string where each individual byte represents a specific action to be performed, such as a V3 swap, wrapping ETH, or interacting with Permit2.
bytes[] calldata inputs: An array of byte strings. Each element in this array contains the ABI-encoded parameters required for the corresponding command in the commands string.
The function logic iterates through each command, dispatches it for execution, and handles the results.
In this loop, for each command byte, the contract retrieves the associated input data and passes both to an internal dispatch function. If a required command fails, the entire transaction reverts.
Dispatcher.solThe dispatch function, located in the Dispatcher.sol contract, serves as the central logic hub of the router. Its sole responsibility is to interpret the command byte and route the execution to the appropriate internal function.
This is implemented as a large series of if / else if statements. Each condition checks the command against a predefined constant. If a match is found, the dispatch function decodes the inputs byte string into the specific parameters needed for that action and calls the relevant implementation function.
As shown in the example, the logic for a V3_SWAP_EXACT_IN involves decoding five distinct parameters from the inputs and calling v3SwapExactInput. In contrast, the V4_SWAP command simply passes the raw inputs data to a more specialized function, _executeActions, which we will explore later.
Commands.solTo ensure code readability and prevent errors from using raw hexadecimal or integer values, all available command identifiers are defined as constants in the Commands.sol library. This file contains a comprehensive list of all actions the router can perform, such as V3_SWAP_EXACT_IN, V2_SWAP_EXACT_IN, WRAP_ETH, PERMIT2_PERMIT, and V4_SWAP.
For a developer to correctly format the inputs array, they need to know the exact parameters and encoding scheme for each command. There are two primary ways to find this information:
Read the Source Code: By inspecting the if block for a specific command within Dispatcher.sol, you can see precisely how the contract decodes the inputs data. The comments and abi.decode or assembly logic reveal the expected data types and order.
Consult the Official Documentation: The Uniswap Docs provide a "Technical Reference" for the Universal Router. This resource explicitly lists every command and its required input parameters.
For example, the documentation specifies that the inputs for a V3_SWAP_EXACT_IN command must be the ABI-encoded bytes of the following parameters in order:
address recipient
uint256 amountIn
uint256 amountOutMin
bytes path
bool payerIsUser
Executing a Uniswap v4 swap through the Universal Router showcases the contract's sophisticated, multi-layered architecture, which relies heavily on contract inheritance and callbacks.
Here is the step-by-step flow:
A user calls execute on UniversalRouter.sol, providing Commands.V4_SWAP as a command.
The execute function calls dispatch in the Dispatcher.sol logic.
The if (command == Commands.V4_SWAP) condition is met, which calls _executeActions(inputs).
The _executeActions function is not defined in Dispatcher but is inherited. The inheritance chain is: Dispatcher inherits from V4SwapRouter, which inherits from V4Router, which inherits from BaseActionsRouter.
The function is found in BaseActionsRouter.sol. It calls poolManager.unlock(unlockData), which is the entry point into the core Uniswap v4 protocol.
The Uniswap v4 PoolManager contract, as a security measure, makes a callback to the contract that called it (the Universal Router).
The router's unlockCallback function, also in BaseActionsRouter.sol, is triggered by the PoolManager. This callback function decodes the actions to be performed.
Inside the callback, _executeActionsWithoutUnlock is called. This function loops through each requested action (e.g., swap, settle).
For each action, it calls _handleAction, which is defined further down the inheritance chain in V4Router.sol.
Finally, _handleAction contains the specific logic to execute V4 actions like Actions.SWAP_EXACT_IN by calling the appropriate functions on the PoolManager.
The Uniswap Universal Router provides a unified and extensible interface for the entire Uniswap protocol suite. Its architecture is built on a simple yet powerful principle: an execute function that processes a list of commands. These commands are parsed by a central Dispatcher, which routes them to the correct implementation logic. For complex integrations like Uniswap v4, the router uses a layered system of inherited contracts and callbacks to interact securely and efficiently with the core protocol.
An architectural breakdown to Understanding the Uniswap Universal Router - Dissect its powerful command-based design that unifies the entire Uniswap ecosystem, and trace the execution flow from the central `dispatch` logic to the v4 `unlockCallback`.
Previous lesson
Previous
Next lesson
Next
Course Overview
About the course
Difference between Uniswap v3 and v4
Uniswap v4 PoolManager
Uniswap v4 Hooks
Uniswap v4 PositionManager
Uniswap v4 Universal Router
Uniswap v4 Singleton architecture and flash accounting
Uniswap v4 operations and lifecycle
Uniswap v4 multihopping and quoting
How to build a Uniswap v4 swap router
How to build a smart contract a liquidation bot executes
Last updated on April 17, 2026
Duration: 6min
Duration: 8min
Duration: 39min
Duration: 35min
Duration: 34min
Duration: 31min
Course Overview
About the course
Difference between Uniswap v3 and v4
Uniswap v4 PoolManager
Uniswap v4 Hooks
Uniswap v4 PositionManager
Uniswap v4 Universal Router
Uniswap v4 Singleton architecture and flash accounting
Uniswap v4 operations and lifecycle
Uniswap v4 multihopping and quoting
How to build a Uniswap v4 swap router
How to build a smart contract a liquidation bot executes
Last updated on April 17, 2026