Skip to main content
Helpful?

Arbitrum Pilot Overview

Starting June 19 2024, the Uniswap team began running a portion of trades on Arbitrum through UniswapX. Unlike UniswapX on mainnet, these orders will have no RFQ portion and thus no exclusivity during the pilot.

Filling on Arbitrum, however, follows the same two steps as filling on Mainnet:

  1. Retrieving signed orders
  2. Filling orders

Retrieving Signed Orders

All signed Dutch Orders on Arbitrum, created through the Uniswap UI will be available via the UniswapX Orders Endpoint. We have swagger documentation but see below for a quick example curl.

GET https://api.uniswap.org/v2/orders?orderStatus=open&chainId=42161&limit=1000

Use the UniswapX SDK to parse the encodedOrder field returned from endpoint. Each one of these Orders represents a fillable user trader.

As a lower latency alternative to polling the API, fillers can also apply to register a webhook and receive a feed of all open orders. See details for registering here

Filling Orders

To execute a discovered order, a filler needs to call the execute method of the Reactor specified in the retrieved encodedOrder body. Currently the Reactor used by the Uniswap interface is located at:

0x1bd1aAdc9E230626C44a139d7E70d842749351eb

Always confirm the address from the retrieved order before submitting.

The simplest fill strategy is called Direct Filler, where the trade is executed directly against tokens held in the fillers address. To use this strategy, a filler can simply approve the order's output tokens to the reactor and call execute or executeBatch from their address. (see source):

// Execute direct filler order
outputToken.approve(reactor, type(uint256).max);
reactor.execute(order);

More sophisticated fillers can implement arbitrarily complex strategies by deploying their own Executor contracts. This contract should implement the IReactorCallback interface, which takes in an order with input tokens and acquires the allotted number of output tokens for the caller. It must approve the output tokens to the reactor, which then transfers them to the order output recipients to settle the order. Executor contracts must call reactor.executeWithCallback or reactor.executeBatchWithCallback. They can also specify arbitrary callback data that will be passed into the reactorCallback call.

contract Executor {
function execute(Order calldata order, bytes calldata callbackData) {
reactor.executeWithCallback(order, callbackData)
}

function reactorCallback(ResolvedOrder[] calldata orders, bytes calldata callbackData) {
// implement strategy here
}
}

// Execute custom fill strategy
address executor = /* Address of deployed executor contract */ ;
bytes fillData = /* Call data to be sent to your executor contract */;
executor.execute(order, fillData);

For convenience, we’ve provided an example Executor Contract which demonstrates how a filler could implement a strategy that executes a UniswapX order against a Uniswap V3 pool. These contracts should be deployed to each chain that the filler would like to support.

Order Types

On Arbitrum, both DutchV2 and DutchV3 order types are supported. You may query for a specific type by specifying the orderType query string parameter:

GET https://api.uniswap.org/v2/orders?orderStatus=open&chainId=42161&limit=1000&orderType={Dutch_V2 | Dutch_V3}

The main difference between the two order types is that DutchV2 orders use a time-based decay while DutchV3 use a block-based decay. Because the block.timestamp of the EVM does not allow for millisecond-level granularity, all decay in DutchV2 must happen in seconds which does not take advantage of Arbitrum's 250 ms block frequency. For this reason, we will migrate Arbitrum to DutchV3 orders over time.

Order Type References

OrderTypeContract AddressReactor SpecificationExample Filler Implementation
DutchV20x1bd1aAdc9E230626C44a139d7E70d842749351ebV2DutchOrderReactor.soluniswapx_strategy.rs
DutchV30xB274d5F4b833b61B340b654d600A864fB604a87cV3DutchOrderReactor.soldutchv3_strategy.rs

Get in touch

Helpful?