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.
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:
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 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"}) {idtimestampamount0Inamount1Inamount0Outamount1Outsendertopair {token0 {idsymbol}token1 {idsymbol}}}}
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.
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.
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: timestamporderDirection: descfirst: 150block: { number: 11009390 }) {idtimestampamount0Inamount1Inamount0Outamount1Outpair {token0 {idsymbol}token1 {idsymbol}}transaction {blockNumber}}}
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: timestamporderDirection: descwhere: { pair: "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11" }) {idtimestampamount0Inamount1Inamount0Outamount1Outpair {token0 {idsymbol}token1 {idsymbol}}}}
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.