Tl;dr
Defi.Money subgraph source code:
Defi.Money subgraphs endpoints:
Why make a subgraph?
Subgraphs are one of many existing solutions to fetch data from the blockchain. Here is a quick rundown of the different ways you can achieve this:
You can get data from RPC frontend calls, which is nice for quickly reading from the chain but can get very workload-heavy on the application side if you need a lot of calls and aggregations
You can get data from read-only helper contracts, which is probably the most neat way to do it but requires a person in the team that can deploy and add new features to the helper contract
You can use a RPC node extension and use a cronjob to frequently export the data to wherever you want, either an api or a database, which is the most manual but gives you the most control of your pipeline
You can use a subgraph, which is an off-the-shelf framework that helps you quickly setup a GraphQL endpoint and query for all contract events, then create aggregations with javascript through custom graphql entities
(there are probably many others not listed here feel free to comment about those)
Here is my table of comparison with opinions on this methods:
As we build defi.money a lot of ideas for features come up: notifications, history, real-time yield ticking every millisecond, many of these ideas required a place where we could quickly read data from, we picked the subgraph because it strikes the best balance for us between low maintance cost:
No need for a smart contract engineer or a person knowledgeable about RPC nodes to maintain the infrastructure
The app team is proficient with javascript
The subgraph CLI defaults deploys a graphql endpoint very fast with all events tracked, which already nailed some of the features for us with very little effort after deploy
Which issues did we solve with our subgraph?
Query events across many instances of the same contract in one chain (for example an AMM contract that has many instances one for each token but all instances are the same code)
Query aggregations of events, which can aggregate values from events from many instances of the same contract (see custom entities in subgraph readme)
Easily deploy this same subgraph structure to all needed chains through GitHub actions
Hosting
When using a subgraph you need to decide where to host it, we went through 2 alternatives when doing defi.money subgraph:
We chose Goldsky because the pricing and customer support were better for our case, and we think open-sourcing the subgraph is enough decentralization for this service, which is only used by the frontend.
Here is a quick view of what we see in our dashboard (will talk more about deploy later):
TheGraph provides a rich ecosystem where you can deploy your subgraph and have it be indexed by a decentralized system, but for a support service for the frontend it's overkill to use it, specially when the frontend itself is already a centralization bottleneck.
TheGraph requires 500 U$ per graph, which can be steep price for a protocol like ours that have many chains and each chain requires 1 subgraph.
Goldsky also provided us with sharper and faster support when answering questions on best practices to deploy for multichain.
Show me the code
How to create any query with LLMs
I think it's worth pointing out that, if you want to quickly create a query to gather data, in the age of LLMs you can simple pass the schema.graphql
to a LLM like claude or gpt and ask it for the query you want, it is as easy as this: Create a graphql query using the provided schema, the query must return the 50 last loans created
Here is this exact prompt in our schema:
then if we try this query (left side) in one of our graphql endpoints we see the live data (right side):
Last Created Loan
Here is a simple query that gets the last loan from a user address in a specific market:
query GetLastLoan($userAddress: String!, $market: String!) {
createLoans(
where: { account: $userAddress, market: $market }
orderBy: blockTimestamp
orderDirection: desc
first: 1
) {
id
market
account
coll_amount
debt_amount
blockTimestamp
}
}
We use this exact query in our frontend to show you your created loan timestamp:
HistoryItem
One of the upsides of using subgraphs is that you can easily leverage custom entities. In this example we created a custom entity called "HistoryItem", every event we want to show in our History Page creates a new HistoryItem, here is how you can fetch the history for an address for example:
query {
historyItems(where: { user: "${address}" }) {
id
type
user
contract_address
market
amount
collateralAmount
debtAmount
receiver
liquidator
guid
dstEid
amountSentLD
amountReceivedLD
epoch
weightAdded
weightRemoved
isLocked
blockNumber
blockTimestamp
transactionHash
}
}
The above query for example lets us build this page in our frontend:
Total Bridged
This is another custom entity we created where we leverage aggregating data, we use LayerZero OmniFungibleToken (OFT) for minting $money, which allows for multi-chain tokens with simpler bridging, whenever they are bridged they emit a OTFSent
event which we use to aggregate and see the complete bridge data for each chain with this query:
query GetTotalBridged {
totalBridged(id: "total") {
total
lastUpdated
}
}
This lets us build the information we show on our monitor desktop dashboard:
Here is where you can find in our code how we do both HistoryItem
and TotalBridged
entities being created from the contract events:
https://github.com/defidotmoney/subgraph/blob/main/src/BridgeToken.ts#L149-L166
How we did deploy
If you see our source code repo actions you will find 3 types of actions: Deploy, Tag, and Delete
When deploying a new subgraph version, we automatically tag the new version as the /dev endpoint, then if tests go well we tag the new version to also be the /prod endpoint and we delete the previous version to avoid extra billing costs for indexing unused subgraphs, we do this separately for each chain as we have some differences between the subgraphs (for example sMoney events only exist in optimism)
The deploy scripts can be easily edited to swap out Goldsky CLI for TheGraph if needed
Have fun
We wrote this article in the hope of illuminating some of the technical decisions we made while dealing with on-chain data, opening the source was our way to give back to any dev trying to solve multichain subgraph issues, we believe we solved some of the most common things so you can just continue and improve from what we did, there is no perfect architecture but it's working nicely for us and we hope it helps someone trying to do the same if you want to build anything around defi.money we are open to chatting with you in our discord!
Upwards and onwards,
Team @defi.money
About defi.money
WELCOME TO THE NEW DIGITAL ERA OF $MONEY