Building an arbitrage opportunity chart with TheGraph subgraph

As part of “Graph Curator prgam”, I created a simple graph demonstrating when the arbitrage opportunities arise between various Defi derivative products and I would like to share some tips I learnt along the way.

  • Graph Curator program
  • IndexCoop DPI Arbitration Bot
  • Dune Analytics vs TheGraph
  • Fighting the quark of Subgraph and AssemblyScript
  • Composability vs Customisation
  • Building Frontend with Recharts.js
  • Were there arbitrage opportunities on DPI?
Image for post
Image for post

Graph Curator program


Developers of decentralized applications need to rely on accurate and efficient APIs but since subgraphs are open-source and can be built by anyone, it can be difficult to identify which subgraphs are high-quality. To solve this, The Graph introduces Curation, to enable information sharing in The Graph ecosystem. Curators are vital to the protocol as they signal on specific subgraphs to indicate to Indexers which data should be indexed.

The key point of being the curators are the deep understanding of how Subgraphs work so we were tasked to build not only subgraph but also created an app which consumes the subgraph. In the previous assignment, curators were asked to evaluate various subgraphs and also list area for opportunities where subgraphs are not written yet. I looked through the list of dapps and decided to tackle IndexCoop, crypto ETF curated by Defi Pulse and built on top of Set Protocol.

Image for post
Image for post

They have very well maintained Handbook with links to all the contracts they use. They have their own smart contract called DPI Index Manager but that contract is more of administrative contract with only dozens of transactions so I did’n find it particularly interesting to index. Their core is DPI token which is one of deployed contracts from SetToken which already has a generic subgraph. However, the existing subgraph didn’t include DPI set for some reason (it may be that DPI is a special contract not deployed from their Core contract which is tracked by the existing subgraph.

IndexCoop DPI Arbitration Bot

While I was researching about set protocol and Index coop DPI, I bumped into DPI Arb Bot Example, a nifty bot to watch the price difference between DPI Token and the sum of their underlying asset, and try to arbitrage using Flash loan!

According to the README, here is how it works

If the weights of the assets included in the index become misaligned with the original index weights described in our methodology, there is an opportunity to arbitrage the index.

The arb bot utilizes flash loans to maintain the index NAV. The use of flash loans allows everyone access and can scale with the size of the arbitrage opportunity. When executed, the arb bot will assess whether the index is outside of the intended +/-5% net asset value (NAV). If it is outside this range, the bot will programatically issue (buy) or redeem (sell) the appropriate underlying assets of the index to keep the index in line with expected NAV and index weights. The current version is optimized to maintain the index weights, not for profit.

This is how it looks like when you run the arb bot.

Image for post
Image for post

As you can see, it shows the price difference of 0.1 %, far from its target of 5%.

I could run this bot for a week to get the historical data to analyse, OR I can actually port this logic into Subgraph.

In most Subgraph, you use eventHandle to watch specific contract events, and write a special handler to respond to the event. However, calculating the price of each underlying assets do not necessarily emit own events. I could use blockHandler which handles every block but it’s known to be very slow to index. What would be the good way? Let’s have a quick look into how the DPI SetToken has been used.

Dune Analytics vs TheGraph

One of my favorite Ethereum Data Analytics tools is Dune Analytics. Dune allows you to write SQL statement and create some nice graphs on the fly.

Even though you do need to request Dune team to index contract specific events , they do already index entire transaction and token transfer info so I can do quick analysis.

What I did was to create a daily aggregate of function counts to see which functions are most used by querying ethereum.transactions table.

The transaction table contains not only the block and transaction hash information but it also includes information such as gas price, gas used, transaction succeeded/failed, etc. I often use this logic to find out average gas price of the contract I interact as you may sometimes don’t know how many transactions and how much gas you need to get into yield farming.

In this case, I grouped by the first 4 character of the data which contains the hash of the function name. I have a quick & dirty script to convert the hash into the function name which I can join with the transaction data.

Image for post
Image for post

The result was somehow unsurprising. The most frequently called function is approve, followed by transfer . It occurs somewhere between 30 ~ 200 times a day, so it’s frequently enough to track often when the token gets exchanged often.

Fighting the quark of Subgraph and AssemblyScript

This is the schema I created

Image for post
Image for post

For every Approve event, I call setToken to get the positions of all the underlying assets (YFI, AAVE, UNI, SNX, and MKR).

let positions = setTokenContract.getPositions()

Then I loop through each asset, get the token price, and multiply by the unit each asset holds. The id of IndexEntity is transaction + number (YFI 1, AAVE 2, etc).

Image for post
Image for post

At the end, I sum it up into tokenSumValue and store it into IndexHistory which sums up all the underlying asset of the DPI token.

The above code I showed is called Assembly Script, which looks very similar to JavaScript and TypeScript, but one part I always get stack is casting into correct data type.

Image for post
Image for post
This is me try&error to do “pow(10, decimal)”

The graph has a detailed conversion table in their documentation page but still somewhat confusing.

Image for post
Image for post

Graphprotocol-utils” by Hadrien Croubois contains a lot of commonly used data type and its casting patterns so highly recommended to clone the repo and use as a reference.

Composability vs Customisation

In addition to calling SetToken , I also called Factory and Pair contract from Uniswap.

Image for post
Image for post

I could have done it at the app level as UniSwap has very extensive Subgraph.

However, their historical token price was hourly and daily aggregate and I wanted to find the price of the specific timing for the arbitration purpose.

This additional call seems the indexing of my subgraph a lot longer (now I have been indexing for more than 7 hrs whereas the prior version took less than a few hours to index).

One of the biggest pain point of Subgraph is it really takes a long time to index. However, I just realised that I can use their Graph playground before the index completes (but http endpoint will be pointing to the existing version until the indexing ends). So I quickly ran a query, copy the query result and pasted into my React app page so that I can start writing the frontend.

Building Frontend with Recharts.js

I have used a couple of charting library but wanted to try something new. While I was researching alternatives, I found out which is used by Uniswap team for their page.

Image for post
Image for post

It’s built on top of SVG and have dependency to . I love d3js’s flexibility and composability but it takes quite an effort to build d3js based charting. Recharts share familiar d3js interface such as domain t.

<XAxis type="number" domain={[0, 'dataMax + 1000']} />

In the example above, you can define the range of yAxis to be somewhere between 0 and max size of the data plus some extra margin.

D3js’s DOM manipulation is known to conflict with the one from React so ot’s also nice to work natively with Reactjs as everything is a react component.

Image for post
Image for post

Adding brush like the above is also a matter of placing <Brush /> component.

Were there arbitrage opportunities on DPI?

It probably took almost a day to index 162k worth of data since the contract was deployed back in mid September.

If I just load the latest 1000 (which is the max you can fetch from subgraph in one request), it only shows the last 5 days worth of data.

Image for post
Image for post

The majority of points are below 0.25%. Looks like there weren’t any chance with 5 % price difference.

However, The Graph’s subgraph query allows you to filter with various criteria like this.

where:{ pctDiff_gt:$pctDiff}

It tells you to return data where the difference is greater than $pctDiff.

I added a slider to be able to filter only the moment when it had more than 5% price difference and it tells me that there were about 26 points which fits the criteria and they were mostly when it fast got started.

Image for post
Image for post

This is the typical “Degen” moment when there is a great APY opportunity but it gets quickly die down. Let’s lower the price difference down to 2% and also use the brush feature to further narrow it down. Looks like there were about 10 minutes (19:37–19:47) of the time window where it had almost 8% price difference when DPI price went down over 30 % .

Image for post
Image for post


In this blog post, I walked through how I developed my dpi-arb-graph bot using Dune Analytics, TheGraph and Rechartjs. I also used filtering feature to pinpoint to the exact point of the arbitrage time opportunities.

You can see all the resources here

I am quite happy with what I have achieved but I am still in the starting point when it comes to doing arbitrage using Flash loan (which was the whole point of the arb bot I got inspiration from).

If you are interested in this like myself, we are organising Aave v2 technical deep dive workshop run by David Truong, one of developers at Aave on Wed, 2nd December. RSVP (by staking 10 xDAI) and join us!

Image for post
Image for post

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store