ENGINEERING
4th October 2020

Retrieving Uniswap trades using The Graph

With the rise of Uniswap, Messari was presented with the challenge of providing real-time and historical data for this thriving AMM (automated market-maker). While data reporting infrastructure around centralized exchanges is relatively mature, the same is not the case for decentralized exchanges.

To solve this issue, we turned to The Graph, which is a decentralized querying protocol that interfaces with Ethereum. Here's how we did it.

Background

Retrieving real-time and historical trade data from Uniswap can be challenging. One way to retrieve trade data is parse out the Swap events emitted from the ERC20 contract: event Swap( address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to );A trade event like this DAI-ETH swap looks as follows:

Notice amount0In is 35000000000000000000, that is a whole lot of DAI! Similarly amount1Out of 103496165542782070 wETH? Am I rich?!

Actually, parsing DAI and wETH smart contracts we find that both ERC20 tokens are specified using 18 decimals:

This means we have to divide by 10^18 in order to retrieve the actual amount0In of 35 DAI swapped for amount1Out0.1034 ETH, dang I’m not rich!

Hence in order to retrieve a single trade on the Uniswap contract, we performed the following:

  1. connect to an Ethereum node and listen to the Swap event from the Uniswap contract
  2. retrieve the raw amount0In, amount1Out values from the event
  3. retrieve the DAI and wETH smart contracts associated to In and Out and parse out the decimal values
  4. convert raw amounts into actual DAI and wETH token amounts by dividing the decimal values

Now imagine we needed to retrieve all DAI-ETH trades since the inception of Uniswap, we’d have to replay all Ethereum blocks from the start of Uniswap’s inception and perform the above steps on all DAI-ETH swap events.

This requires an in-depth ETL infrastructure investment.

The Graph

The Graph provides a GraphQL abstraction on top of the Ethereum network, allowing consumers to retrieve normalized trades from Uniswap.

The same DAI-ETH pair can be retrieved using the following query inside the playground:

query swaps {
swaps(
where: {
transaction: "0x5ed0ee8ff25e0a368519ba10822d2f1d4261ca8cf3fe42b4f5806a515865d88f"
}
) {
id
timestamp
amount0In
amount1In
amount0Out
amount1Out
sender
to
pair {
token0 {
id
symbol
}
token1 {
id
symbol
}
}
}
}

Notice the normalized amount0In of 35 DAI swapped for amount1Out0.1034 ETH gets returned!

This means we no longer have to retrieve the individual Swap events and extrapolate the relevant with the referencing DAI and wETH smart contracts.

Additionally, the above query returns the id and symbol of the tokens that have been swapped, within the pair sub-object. This provides a handy visual reference and allows us to audit the normalization process.

Under the hood

As mentioned earlier, The Graph uses GraphQL to interface with the Ethereum network. For folks who aren’t familiar, GraphQL is a query framework mapping a user query to one or more backend queries, allowing the user to work on a higher level of abstraction without knowing the internals of the underlying system.

Contrast the above single GraphQL query swaps(where:{transaction:"..."}) to the four step process in monitoring the on-chain Swap (index_topic_1 address sender, uint256 amount0In, ...) event.

This mapping is maintained within the Uniswap V2 Subgraph repository by the Uniswap team. Where the mapping dictates which events from the ERC20 contract should be extracted, transformed, and stored. It also provides a clear and concise abstraction for the user to query the stored data and consume it in a useful way.

In fact, https://info.uniswap.org/ is powered by this subgraph using this exact abstraction.

Historical Data

To retrieve historical DAI-ETH data, we can specify the number of historical trades we want via first: 150, starting at a given block via block: {number:11009390} ordered descending by the time traded via orderBy: timestamp, orderDirection: desc

query trades {
swaps(
where: { pair: "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11" }
orderBy: timestamp
orderDirection: desc
first: 150
block: { number: 11009390 }
) {
id
timestamp
amount0In
amount1In
amount0Out
amount1Out
pair {
token0 {
id
symbol
}
token1 {
id
symbol
}
}
transaction {
blockNumber
}
}
}

Real-Time Data

To get real-time DAI-ETH trades, change query to subscription. Under the hood this uses a websocket connection to push new trades to the consumer when a swap has happened on-chain:

subscription swaps {
swaps(
orderBy: timestamp
orderDirection: desc
where: { pair: "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11" }
) {
id
timestamp
amount0In
amount1In
amount0Out
amount1Out
pair {
token0 {
id
symbol
}
token1 {
id
symbol
}
}
}
}

Come work with us!

If you’re a software engineer interested in helping us contextualize and categorize the world’s crypto data, we’re hiring. Check out our open engineering positions to find out more.