UniswapX is an ERC20 swap settlement protocol that provides swappers with a gasless experience, MEV protection, and access to arbitrary liquidity sources. Swappers generate signed orders which specify the specification of their swap, and fillers compete using arbitrary fill strategies to satisfy these orders.
Order Reactors settle UniswapX orders. They are responsible for validating orders of a specific type, resolving them into inputs and outputs, and executing them against the filler's strategy, and verifying that the order was successfully fulfilled.
Reactors process orders using the following steps:
- Validate the order
- Resolve the order into inputs and outputs
- Pull input tokens from the swapper to the fillContract using permit2
permitWitnessTransferFromwith the order as witness - Call
reactorCallbackon the fillContract - Transfer output tokens from the fillContract to the output recipients
Reactors implement the IReactor interface which abstracts the specifics of the order specification. This allows for different reactor implementations with different order formats to be used with the same interface, allowing for shared infrastructure and easy extension by fillers.
Current reactor implementations:
- LimitOrderReactor: A reactor that settles simple static limit orders
- DutchOrderReactor: A reactor that settles linear-decay dutch orders
- ExclusiveDutchOrderReactor: A reactor that settles linear-decay dutch orders with a period of exclusivity before decay begins
Order fillContracts fill UniswapX orders. They specify the filler's strategy for fulfilling orders and are called by the reactor with reactorCallback when using executeWithCallback or executeBatchWithCallback.
Some sample fillContract implementations are provided in this repository:
- SwapRouter02Executor: A fillContract that uses UniswapV2 and UniswapV3 via the SwapRouter02 router
If a filler wants to simply fill orders using funds held by an address rather than using a fillContract strategy, they can do so gas efficiently by using execute or executeBatch. These functions cause the reactor to skip the reactorCallback and simply pull tokens from the filler using msg.sender.
Jump to the docs for Creating a Filler Integration.
| Contract | Address | Source |
|---|---|---|
| V2 Dutch Order Reactor | 0x00000011F84B9aa48e5f8aA8B9897600006289Be | V2DutchOrderReactor |
| Exclusive Dutch Order Reactor | 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4 | ExclusiveDutchOrderReactor |
| OrderQuoter | 0x54539967a06Fc0E3C3ED0ee320Eb67362D13C5fF | OrderQuoter |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Permit2 |
| Contract | Address | Source |
|---|---|---|
| Priority Order Reactor | 0x000000001Ec5656dcdB24D90DFa42742738De729 | PriorityOrderReactor |
| OrderQuoter | 0x88440407634f89873c5d9439987ac4be9725fea8 | OrderQuoter |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Permit2 |
| Contract | Address | Source |
|---|---|---|
| V3 Dutch Order Reactor | 0x00000000fc1E66C9f582566EAd00108e55F1c0C6 | V3DutchOrderReactor |
| OrderQuoter | 0x00000000a3db63Df9078cBF3dF88B4CAdD5a7F58 | OrderQuoter |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Permit2 |
Both V3DutchOrderReactor and OrderQuoter were deployed via the canonical Arachnid CREATE2 factory (0x4e59b44847b379578588920cA78FbF26c0B4956C) using salts mined with create2crunch β the reactor address has 4 leading zero bytes (5 total) and the quoter has 4 leading zero bytes, saving ~12 gas per zero byte each time those addresses are encoded in calldata.
UniswapX is being deployed on Tempo (chainId 4217) via V3DutchOrderReactor. Tempo has several EVM-level quirks that integrators and fillers must understand before building or running orders against it:
- ERC20-only β no native token. Tempo has no native gas token. Orders MUST NOT use the
NATIVEsentinel address (address(0)) for either the input token or any output token. Both swappers and fillers are expected to operate strictly in ERC20s. CALLVALUE/BALANCE/SELFBALANCEalways return 0. Thepayablemodifier on reactor entry points (execute,executeBatch,executeWithCallback,executeBatchWithCallback) is a no-op on Tempo because no value can ever be transferred. The leftover-balance refund branch inBaseReactor.sol:126(theaddress(this).balance > 0refund path) is dead code on Tempo β it is harmless but will never execute.block.basefeeis a constant. On Tempo,block.basefeeis fixed at approximately2e10in attodollars per gas (note: attodollars, not wei). The V3 gas-adjustment math in_updateWithGasAdjustmentremains internally consistent as long as the cosigner setsstartingBaseFee = block.basefeeandadjustmentPerGweiBaseFee = 0. SettingadjustmentPerGweiBaseFee = 0makes the gas adjustment unambiguously a no-op and is the recommended cosigner configuration for V3 orders on Tempo.block.numberis a standard monotonic counter.BlockNumberish.solrequires no changes for Tempo. Block time is roughly 500ms.- Sample executors are ERC20-only on Tempo. The sample fillers in this repo β
UniversalRouterExecutor,SwapRouter02Executor, andMultiFillerSwapRouter02Executorβ perform native sweeps viaaddress(this).balance. Those sweep paths are inert on Tempo (the balance is always 0). PMMs and other fillers building executors for Tempo MUST implement ERC20-balance-based sweeps instead of relying on native-balance logic.
The canonical Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3) is deployed on Tempo, and V3DutchOrderReactor binds to it via script/DeployDutchV3.s.sol. See that script's header comment for deploy invocation details.
# install dependencies
forge install
# compile contracts
forge build
# run unit tests
forge test
# run integration tests
FOUNDRY_PROFILE=integration forge test
Note that UniswapX handles fee-on-transfer tokens by transferring the amount specified to the recipient. This means that the actual amount received by the recipient will be after fees.
| Version Number | Commit | Contract Address |
|---|---|---|
| 1.0 | 597cf617dd6d32b3f181edbc37aed11bc5648d93 | Contract no longer in use. Read more about the bug here. |
| 1.1 | cf53fc7dd48029a9189d26812d676a4ea9d08d6c | 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4 |
| 2.0 | 4bacf632512ec5c9504a78ad1b7e1aec7efc6767 | 0x00000011f84b9aa48e5f8aa8b9897600006289be |
This repository is subject to the Uniswap Labs Bug Bounty program, per the terms defined here.
