While building dApps, one runs into one problem fairly quickly. While it is cheap to query Ethereum nodes for the current state of a contract, e.g., the current balance of a user, an archive node is needed to gather past transactions, such as a user performing a token swap.
With the number of such transactions constantly growing, it becomes more and more inefficient to do those operations on the fly.
Subgraphs (powered by the Graph Network) alleviate this problem by indexing data block by block, transforming it, and offering a simple API to query.
The following article is about the creation and intention of a Subgraph based on the one created for our new product BentoBox Lending. The source code for this project may be found here: github.com/sushiswap/bentobox-subgraph.
About BentoBox Lending
BentoBox is the first product built upon SushiSwap’s new architecture, BentoBox, which allows significant improvements in user experience and gas efficiency.
BentoBox Lending is a risk-isolated solution. This is the first major distinction to existing protocols. The most important aspects are as follows:
- Anyone can create a lending pair with one asset token and one collateral token with a specific oracle.
- You can earn interest by supplying the asset.
- You can borrow the asset by supplying collateral.
- The target utilization is between 70–80%. This is the percentage of assets that are borrowed from the total supplied.
- An elastic interest rate moves up if the pair is underutilized and down if it’s over-utilized. The further it is from the target, the faster the interest rate will move. At 100% utilization, the interest rate will double every 8 hours.
Interest rate changes compound when accrue is called, then it will move faster. This is the linear time when no transactions happen on the pair.
- When a user is up to 2% insolvent, the position can be liquidated against any approved ‘swapper.’ Currently, there is one approved swapper for SushiSwap. The liquidation proceeds go 90% to the asset suppliers, 9% to the SushiBar, and 1% to the developer.
- When a user is more than 2% insolvent, any user can liquidate the position and get the full 12% liquidation fee. There are several ways to liquidate, including a flash liquidation (or optimistic liquidation) that will give you the collateral. You have to repay the debt before the end of the transaction.
- Short functionality allows you to leverage a position by borrowing any asset amount, swapping it to collateral, and depositing the collateral. As long as your position is solvent in the end. Unwind does the opposite.
Designing a subgraph
The first step in constructing any database is to think about what queries are necessary.
What information someone might need to know. Similarly to a DEX, one such query would be to get all deployed pairs.
With this knowledge in mind, the next step is to conceptualize what entities are needed to store this data, graphs like the one below help to optimize these and understand the relations.
After a first concept has been decided on, one can translate this visualization into a GraphQL schema.
The example below describes the BentoBox entity. The relations to other entities allow querying all connected information.
At this point, we have data entity templates but provided no instruction on how they should be filled with the data produced by interactions with our smart contracts.
Using a factory pattern with BentoBox deploying lending pairs, we utilize data source templates to keep track of new instances. As the number of those can grow almost infinitely, it would be inefficient to run data updates on every block for every pair.
The most efficient way to manipulate a SubGraph’s data is to react to events emitted by the contract whenever there is a change.
What events the SubGraph should keep track of is defined in the subgraph.yaml file.
In this case, if the LogFeeTo method is fired on a lending pair, the method handleLogFeeTo inside pair.ts is called.
Looking inside pair.ts, one can see the entity LendingPair being called into memory, with the identifier being the contract’s address. Three properties of it, feeTo, block, and timestamp are updated.
When should a Subgraph be designed?
As alluded to earlier, a subgraph for any active project can only run efficiently to react to events. As those events need to be emitted inside the smart contract, it makes sense to plan the SubGraph alongside the development to ensure all necessary data can be fetched through events.