<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>balakhonoff</title>
        <link>https://paragraph.com/@balakhonoff</link>
        <description>Product Lead @ChainstackHQ / Data Science Expert - Building Web3 Data APIs / Blockchain Indexing Solutions / Subgraphs</description>
        <lastBuildDate>Fri, 10 Apr 2026 18:34:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>balakhonoff</title>
            <url>https://storage.googleapis.com/papyrus_images/b946461c3fce15fdc9a39ad2929daeced40b6a52378ea9b822d21a6d05a8d0e1.jpg</url>
            <link>https://paragraph.com/@balakhonoff</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[How to access real-time smart contract data from Python code (using Lido contract as an example)]]></title>
            <link>https://paragraph.com/@balakhonoff/how-to-access-real-time-smart-contract-data-from-python-code-using-lido-contract-as-an-example</link>
            <guid>cNY52WHi3OKbsVCyxb1K</guid>
            <pubDate>Mon, 05 Jun 2023 14:48:13 GMT</pubDate>
            <description><![CDATA[Let’s imagine you need access to the real-time data of some smart contracts on Ethereum (or Polygon, BSC, etc.) like Uniswap or even PEPE coin to analyze its data using the standard data scientist/analyst tools: Python, Pandas, Matplotlib, etc. In this tutorial, I’ll show you more sophisticated data access tools that are more like a surgical scalpel (The Graph subgraphs) than a well-known Swiss knife (RPC node access) or hammer (ready-to-use APIs). I hope my metaphors don’t scare you 😅.There...]]></description>
            <content:encoded><![CDATA[<p>Let’s imagine you need access to the real-time data of some smart contracts on Ethereum (or Polygon, BSC, etc.) like Uniswap or even PEPE coin to analyze its data using the standard data scientist/analyst tools: Python, Pandas, Matplotlib, etc. In this tutorial, I’ll show you more sophisticated data access tools that are more like a surgical scalpel (The Graph subgraphs) than a well-known Swiss knife (RPC node access) or hammer (ready-to-use APIs). I hope my metaphors don’t scare you 😅.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/052a5f40f0959c2432bb5adc9c275a6dabd65942d0b21f5bec160b1631213670.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>There are some different methods how to access the data on Ethereum:</p><ul><li><p>Using RPC-node commands like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chainstack.com/reference/ethereum-getblockbynumber">getBlockByNumber</a> to get the low-level block information, then accessing smart contract data via libraries like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://web3py.readthedocs.io/en/stable/">web3.py</a>. It allows you to get the data block by block and then collect it in your own database or CSV file. This way is not really fast, and parsing popular smart contract data using this way usually takes years.</p></li><li><p>Using some data analytics providers like Dune which can help you with some popular smart contract data, but it is not really real-time. The latency can be about several minutes.</p></li><li><p>Using some ready-to-use APIs like NFT API/Token API/DeFi API. And it can be an excellent option because usually, the latency is low. The only problem that you can face is that the data you need is not available. For instance, not all variables can be available as historical time series.</p></li></ul><p>What if you still want to have real-time data of a smart contract, but are not satisfied by previous solutions, because you want all:</p><ul><li><p>you want low-latency data (the data is always up-to-date right after a new block has been mined)</p></li><li><p>you need a custom slice of data that is not available on any ready-to-use API</p></li><li><p>you don’t want to hassle with manual processing data block by block along with processing block reorganizations</p></li></ul><p>This is the best use case for subgraphs by The Graph. Essentially, The Graph is a decentralized network to access smart contract data in a decentralized way paying the price for requests in GRT tokens.</p><p>But the underlying technology called “subgraphs” allows you to transform your simple description of what variables need to be saved (for real-time access) into the production-grade ETL pipeline which</p><ul><li><p>Extract data from the blockchain</p></li><li><p>Saves it into the database</p></li><li><p>Making this data accessible via GraphQL interface</p></li><li><p>Updates the data after each new block is mined on the network</p></li><li><p>Automatically process the chain reorganizations</p></li></ul><p>This is a big deal. You don’t need to be a highly qualified data engineer experienced in EVM-compatible blockchains to set up the entire workflow.</p><p>But let’s start with something ready-to-use. What if somebody has already developed a subgraph that helps to access the data you need?</p><p>You can go to The Graph hosted service <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://thegraph.com/hosted-service">website</a>, find the community subgraph section and try to get the existing subgraphs on the protocol you need. For instance, let’s find a subgraph to access <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://lido.fi/">Lido</a> protocol (which allows users to stake their Ethers without limiting the minimum value of 32 ethers, as is usually the case, and apart from it getting the tokens that can be staked again, can you believe this?😅).</p><p>Lido protocol is currently top-1 in terms of TVL (Total Value Locked — a metric used to measure the total value of digital assets that are locked or staked in a particular DeFi platform or DApp) according to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://defillama.com/">DeFiLlama</a>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a81edfcf801157115e956287053a2653bd1f946eb316b59f7565f7d068c54fb2.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>And there it is! The subgraph made by Lido team is here.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f8fba56d1bb2fbebdd49f112b0675dd242ee543b8dd01e5593fcf1b4e824623e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Let’s go to the subgraph details page.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4c8990899663e7e32ccfafff2da4e227858e60b623fc833c0abd8f8e21ed2cd9.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>What can we see here?</p><ol><li><p>The IPFS CID of this subgraph — it is an internal unique identifier of this subgraph pointing to the manifest of this subgraph on the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ipfs.tech/">IPFS</a> (peer-to-peer protocol to find a file by hash — this is a critically simplified explanation, but you can figure out how it works for yourself).</p></li><li><p>Query URL — this is an actual endpoint that we will use in our Python code to access smart contract data.</p></li><li><p>The indicator of a subgraph sync status. When the subgraph is up-to-date you can query the data, but you should understand that deploying a new subgraph you must wait for a while to sync it. During this process, it will show the number of current block under processing.</p></li><li><p>The GraphQL query. Each subgraph has its own data structure (the list of tables with data) that need to be considered when creating a GraphQL query. Generally, GraphQL is pretty easy to learn, but if you are struggling with it you can ask ChatGPT to help with it 🙂. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/p/5cb4349fbf9e">Here</a> is an example at the end of the article.</p></li><li><p>The button which runs the query.</p></li><li><p>The output window. As you can see the GraphQL response is JSON-like structure.</p></li><li><p>A switch that allows you to see the data structure of this subgraph.</p></li></ol><p>Let’s get down to business (Let’s uncover our jupyter notebooks🙂).</p><p>Getting raw data:</p><pre data-type="codeBlock" text="import pandas as pd
import requests

def run_query(uri, query):
    request = requests.post(uri, json={&apos;query&apos;: query}, headers={&quot;Content-Type&quot;: &quot;application/json&quot;})
    if request.status_code == 200:
        return request.json()
    else:
        raise Exception(f&quot;Unexpected status code returned: {request.status_code}&quot;)

url = &quot;https://api.thegraph.com/subgraphs/name/lidofinance/lido&quot;
query = &quot;&quot;&quot;{
  lidoTransfers(first: 50) {
    from
    to
    value
    block
    blockTime
    transactionHash
  }
}&quot;&quot;&quot;

result = run_query(url, query)
  
"><code>import pandas as pd
import requests

def run_query(uri, query):
    <span class="hljs-attr">request</span> = requests.post(uri, json={<span class="hljs-string">'query'</span>: query}, headers={<span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>})
    if <span class="hljs-attr">request.status_code</span> == <span class="hljs-number">200</span>:
        return request.json()
    else:
        raise Exception(f"Unexpected status code returned: {request.status_code}")

<span class="hljs-attr">url</span> = <span class="hljs-string">"https://api.thegraph.com/subgraphs/name/lidofinance/lido"</span>
<span class="hljs-attr">query</span> = <span class="hljs-string">"""{
  lidoTransfers(first: 50) {
    from
    to
    value
    block
    blockTime
    transactionHash
  }
}"""</span>

<span class="hljs-attr">result</span> = run_query(url, query)
  
</code></pre><p>The result variable looks like this:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d3dffa4ac130c56ec3b04a22e509dcecdec463bf9df75cab555fc966ffdec813.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>And the last transformation (which is only valid for flat JSON response) creates a dataframe:</p><pre data-type="codeBlock" text="df = pd.DataFrame(result[&apos;data&apos;][&apos;lidoTransfers&apos;])
df.head()
"><code>df <span class="hljs-operator">=</span> pd.DataFrame(result[<span class="hljs-string">'data'</span>][<span class="hljs-string">'lidoTransfers'</span>])
df.head()
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b01df2c015d39668331c47493250b963a6beec1d5849d32eb3ca71ec7b994a2c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>But how to download all data from the table? With GraphQL there are different options on it, and I am picking the following one. Considering that the blocks are ascending let’s scan from the first block querying by 1000 entities each time (1000 is the limit for the graph-node).</p><pre data-type="codeBlock" text="query = &quot;&quot;&quot;{
  lidoTransfers(orderBy: block, orderDirection: asc, first: 1) {
     block
  }
}&quot;&quot;&quot;
# here we get the first block number to start with
first_block = int(run_query(url, query)[&apos;data&apos;][&apos;lidoTransfers&apos;][0][&apos;block&apos;])
current_last_block = 17379510

#query template to make consecutive queries
query_template = &quot;&quot;&quot;{{
  lidoTransfers(where: {{block_gte: {block_x} }}, orderBy: block, orderDirection: asc, first: 1000) {{
    from
    to
    value
    block
    blockTime
    transactionHash
  }}
}}&quot;&quot;&quot;

result = [] # storing the response
offset = first_block # starting from the first found block

while True: 
    query = query_template.format(block_x=offset) # generate the query
    sub_result = run_query(url, query)[&apos;data&apos;][&apos;lidoTransfers&apos;] # get the data
    if len(sub_result)&lt;=1: # break if finished
        break
    sh = int(sub_result[-1][&apos;block&apos;]) - offset # calculate the shift
    offset = int(sub_result[-1][&apos;block&apos;]) # calculate the new shift
    result.extend(sub_result) # append
    print(f&quot;{(offset-first_block)/(current_last_block - first_block)* 100:.1f}%, got {len(sub_result)} lines, block shift {sh}&quot; ) #show the log

# convert to the dataframe
df = pd.DataFrame(result)
"><code><span class="hljs-attr">query</span> = <span class="hljs-string">"""{
  lidoTransfers(orderBy: block, orderDirection: asc, first: 1) {
     block
  }
}"""</span>
<span class="hljs-comment"># here we get the first block number to start with</span>
<span class="hljs-attr">first_block</span> = int(run_query(url, query)[<span class="hljs-string">'data'</span>][<span class="hljs-string">'lidoTransfers'</span>][<span class="hljs-number">0</span>][<span class="hljs-string">'block'</span>])
<span class="hljs-attr">current_last_block</span> = <span class="hljs-number">17379510</span>

<span class="hljs-comment">#query template to make consecutive queries</span>
<span class="hljs-attr">query_template</span> = <span class="hljs-string">"""{{
  lidoTransfers(where: {{block_gte: {block_x} }}, orderBy: block, orderDirection: asc, first: 1000) {{
    from
    to
    value
    block
    blockTime
    transactionHash
  }}
}}"""</span>

<span class="hljs-attr">result</span> = [] <span class="hljs-comment"># storing the response</span>
<span class="hljs-attr">offset</span> = first_block <span class="hljs-comment"># starting from the first found block</span>

while True: 
    <span class="hljs-attr">query</span> = query_template.format(block_x=<span class="hljs-literal">off</span>set) <span class="hljs-comment"># generate the query</span>
    <span class="hljs-attr">sub_result</span> = run_query(url, query)[<span class="hljs-string">'data'</span>][<span class="hljs-string">'lidoTransfers'</span>] <span class="hljs-comment"># get the data</span>
    if len(sub_result)&#x3C;=1: <span class="hljs-comment"># break if finished</span>
        break
    <span class="hljs-attr">sh</span> = int(sub_result[-<span class="hljs-number">1</span>][<span class="hljs-string">'block'</span>]) - <span class="hljs-literal">off</span>set <span class="hljs-comment"># calculate the shift</span>
    <span class="hljs-attr">offset</span> = int(sub_result[-<span class="hljs-number">1</span>][<span class="hljs-string">'block'</span>]) <span class="hljs-comment"># calculate the new shift</span>
    result.extend(sub_result) <span class="hljs-comment"># append</span>
    print(f"{(offset-first_block)/(current_last_block - first_block)* 100:.1f}%, got {len(sub_result)} lines, block shift {sh}" ) <span class="hljs-comment">#show the log</span>

<span class="hljs-comment"># convert to the dataframe</span>
<span class="hljs-attr">df</span> = pd.DataFrame(result)
</code></pre><p>Bear in mind that we do overlapping queries because each time we use the last block number from the query to start the next one with. We do this to avoid missing records due to the possible multiple transactions per block.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/69b3e65f6fa0582570630b02371167d48351d54e9a6c2dd8bf7fcf2e34e64beb.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>As we see each query returns 1000 lines, but the block number is shifting by several tens of thousands. It means that not every block contains at least one Lido transaction. An important step here is to get rid of the duplicates that we collected avoiding missing records:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/409c63feab188fa2731347baa0f5dc793f417c4ca4430d64e9cf3986c0e1fe90.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>As we see, there are 9k+ duplicated lines in the dataframe.</p><p>Now let’s do some simple EDA.</p><pre data-type="codeBlock" text="col = &quot;from&quot;
df.groupby(col, as_index=False)\
    .agg({&apos;transactionHash&apos;: &apos;count&apos;})\
    .sort_values(&apos;transactionHash&apos;, ascending=False)\
    .head(5)
"><code>col <span class="hljs-operator">=</span> <span class="hljs-string">"from"</span>
df.groupby(col, as_index<span class="hljs-operator">=</span>False)\
    .agg({<span class="hljs-string">'transactionHash'</span>: <span class="hljs-string">'count'</span>})\
    .sort_values(<span class="hljs-string">'transactionHash'</span>, ascending<span class="hljs-operator">=</span>False)\
    .head(<span class="hljs-number">5</span>)
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f8abdc3bbf205e941b6c0780169876ee0a65f6217cab9bc7d80647c50e1e0fc5.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>If we check the most frequent addresses in the field “from” we find “0x0000000000000000000000000000000000000000” address. Usually, it means the issue of new tokens, so we can find a transaction in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/tx/0x63a36a672165dc6b6d3a6f6d6594b79720f0cb844c4876e31b62ed3b6ee79706">Etherscan</a> and check:</p><pre data-type="codeBlock" text="(df[df[&apos;from&apos;]==&apos;0x0000000000000000000000000000000000000000&apos;].iloc[1000].to,\
df[df[&apos;from&apos;]==&apos;0x0000000000000000000000000000000000000000&apos;].iloc[1000].transactionHash,
df[df[&apos;from&apos;]==&apos;0x0000000000000000000000000000000000000000&apos;].iloc[1000].value,)
"><code>(df[df[<span class="hljs-string">'from'</span>]<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-string">'0x0000000000000000000000000000000000000000'</span>].iloc[<span class="hljs-number">1000</span>].to,\
df[df[<span class="hljs-string">'from'</span>]<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-string">'0x0000000000000000000000000000000000000000'</span>].iloc[<span class="hljs-number">1000</span>].transactionHash,
df[df[<span class="hljs-string">'from'</span>]<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-string">'0x0000000000000000000000000000000000000000'</span>].iloc[<span class="hljs-number">1000</span>].<span class="hljs-built_in">value</span>,)
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6e1c9ca827dfcfa84b83ec31b3183f4f5a8130174f689035681f02513138d52b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>We will see the transaction that has the same “value”:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/102fe3b9aae272fc804ed0fca025afe2c83df92eca88b117b5e486e2be7e7c7f.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Also, it is interesting to check the most frequent receiver of funds (by the field called “to”):</p><pre data-type="codeBlock" text="col = &quot;to&quot;
df.groupby(col, as_index=False)\
    .agg({&apos;transactionHash&apos;: &apos;count&apos;})\
    .sort_values(&apos;transactionHash&apos;, ascending=False)\
    .head(5)
"><code>col <span class="hljs-operator">=</span> <span class="hljs-string">"to"</span>
df.groupby(col, as_index<span class="hljs-operator">=</span>False)\
    .agg({<span class="hljs-string">'transactionHash'</span>: <span class="hljs-string">'count'</span>})\
    .sort_values(<span class="hljs-string">'transactionHash'</span>, ascending<span class="hljs-operator">=</span>False)\
    .head(<span class="hljs-number">5</span>)
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f23b2e5df0f311da2bb6c45dd696e7d131cdfef1fb25a98b0f1b378dd4d15854.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The address “0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0” can be found on the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0#code">Etherscan</a> as Lido: wrapped stETH Token.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c6666faeb618a60e92c28384aff834c2cf99bc5a9459c3d8b4f66b1d41b8cb1f.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Now let’s see the number of transactions per month:</p><pre data-type="codeBlock" text="import datetime
import matplotlib.pyplot as plt

df[&quot;blockTime_&quot;] = df[&quot;blockTime&quot;].apply(lambda x: datetime.datetime.fromtimestamp(int(x)))
df[&apos;ym&apos;] = df[&apos;blockTime_&apos;].dt.strftime(&quot;%Y-%m&quot;)
df_time = df.groupby(&apos;ym&apos;, as_index=False).agg({&apos;transactionHash&apos;: &apos;count&apos;}).sort_values(&apos;ym&apos;)

fig, ax = plt.subplots(figsize=(12,8))
ax.plot(df_time[&apos;ym&apos;].iloc[:-1], df_time[&apos;transactionHash&apos;].iloc[:-1])
plt.xticks(rotation=45)
plt.xlabel(&apos;month&apos;)
plt.ylabel(&apos;number of transactions&apos;)
plt.grid()
plt.show()
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">datetime</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">matplotlib</span>.<span class="hljs-title">pyplot</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">plt</span>

<span class="hljs-title">df</span>[<span class="hljs-string">"blockTime_"</span>] <span class="hljs-operator">=</span> <span class="hljs-title">df</span>[<span class="hljs-string">"blockTime"</span>].<span class="hljs-title">apply</span>(<span class="hljs-title">lambda</span> <span class="hljs-title">x</span>: <span class="hljs-title">datetime</span>.<span class="hljs-title">datetime</span>.<span class="hljs-title">fromtimestamp</span>(<span class="hljs-title"><span class="hljs-keyword">int</span></span>(<span class="hljs-title">x</span>)))
<span class="hljs-title">df</span>[<span class="hljs-string">'ym'</span>] <span class="hljs-operator">=</span> <span class="hljs-title">df</span>[<span class="hljs-string">'blockTime_'</span>].<span class="hljs-title">dt</span>.<span class="hljs-title">strftime</span>(<span class="hljs-string">"%Y-%m"</span>)
<span class="hljs-title">df_time</span> <span class="hljs-operator">=</span> <span class="hljs-title">df</span>.<span class="hljs-title">groupby</span>(<span class="hljs-string">'ym'</span>, <span class="hljs-title">as_index</span><span class="hljs-operator">=</span><span class="hljs-title">False</span>).<span class="hljs-title">agg</span>({<span class="hljs-string">'transactionHash'</span>: <span class="hljs-string">'count'</span>}).<span class="hljs-title">sort_values</span>(<span class="hljs-string">'ym'</span>)

<span class="hljs-title">fig</span>, <span class="hljs-title">ax</span> <span class="hljs-operator">=</span> <span class="hljs-title">plt</span>.<span class="hljs-title">subplots</span>(<span class="hljs-title">figsize</span><span class="hljs-operator">=</span>(12,8))
<span class="hljs-title">ax</span>.<span class="hljs-title">plot</span>(<span class="hljs-title">df_time</span>[<span class="hljs-string">'ym'</span>].<span class="hljs-title">iloc</span>[:<span class="hljs-operator">-</span>1], <span class="hljs-title">df_time</span>[<span class="hljs-string">'transactionHash'</span>].<span class="hljs-title">iloc</span>[:<span class="hljs-operator">-</span>1])
<span class="hljs-title">plt</span>.<span class="hljs-title">xticks</span>(<span class="hljs-title">rotation</span><span class="hljs-operator">=</span>45)
<span class="hljs-title">plt</span>.<span class="hljs-title">xlabel</span>(<span class="hljs-string">'month'</span>)
<span class="hljs-title">plt</span>.<span class="hljs-title">ylabel</span>(<span class="hljs-string">'number of transactions'</span>)
<span class="hljs-title">plt</span>.<span class="hljs-title">grid</span>()
<span class="hljs-title">plt</span>.<span class="hljs-title">show</span>()
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3d26e95b1e2a8a6006f952889b09dafd665ed4d4d7f859c452f6444d89bb6ceb.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The number of transactions per month is growing over the years constantly!</p><p>You can continue your investigation going forward with the other fields or make other queries to different tables on the same subgraph:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/412a906c8791aeffa940e049e2de2a78aab4277a5b8dbbb87712d8e761adb8f0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-what-else-you-can-do-with-subgraphs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What else you can do with subgraphs</h3><p><strong>Firstly</strong>, if you need to access the data of <strong>any other smart contract</strong> <strong>that has no subgraph yet</strong> (or the data is not enough) you can easily write your own subgraph using these tutorials</p><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@balakhonoff_47314/how-to-access-the-tornado-cash-data-easily-using-the-graphs-subgraphs-a70a7e21449d">How to access the Tornado Cash data easily using The Graph’s subgraphs</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@balakhonoff_47314/tutorial-how-to-access-transactions-of-pepe-pepe-coin-using-the-graph-subgraphs-and-chatgpt-5cb4349fbf9e">How to access transactions of PEPE ($PEPE) coin using The Graph subgraphs and ChatGPT prompts</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chainstack.com/docs/subgraphs-tutorial-indexing-uniswap-data">Indexing Uniswap data with subgraphs</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chainstack.com/docs/subgraphs-tutorial-a-beginners-guide-to-getting-started-with-the-graph">A beginner’s guide to getting started with The Graph</a></p></li></ol><p><strong>Secodly, i</strong>f you want to become an advanced subgraphs developer, consider these deep-diving tutorials:</p><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chainstack.com/docs/subgraphs-tutorial-working-with-schemas">Explaining Subgraph schemas</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chainstack.com/docs/subgraphs-tutorial-debug-subgraphs-with-a-local-graph-node">Debugging subgraphs with a local Graph Node</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chainstack.com/docs/subgraphs-tutorial-fetching-subgraph-data-using-javascript">Fetching subgraph data using javascript</a></p></li></ol><p><strong>Thirdly</strong>, if you are going to use subgraphs real-time data in your production application, you can deploy your subgraph into the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chainstack.com/subgraphs/">Chainstack Subgraphs</a> hosted service which is multiple times faster and 99.9% reliable.</p><p><strong>Finally</strong>, if you still don’t feel comfortable with subgraphs do not hesitate to ask any questions in the telegram chat “<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://t.me/+HHP9q2gWFGNlNGYy">Subgraphs Experience Sharing</a>”.</p><p><strong>Additionally</strong>, if you have any questions regarding smart contract development, join the chat “<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://t.me/dev_solidity">Solidity Development</a>”</p><p>—</p><p>Kirill Balakhonov — Product Lead @ <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chainstack.com/">chainstack.com</a> — <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/balakhonoff">Twitter</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://t.me/kirill_balakhonov">Telegram</a></p><p>This article is written based on advice from the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://t.me/eth_ru">@eth_ru</a> chat</p>]]></content:encoded>
            <author>balakhonoff@newsletter.paragraph.com (balakhonoff)</author>
        </item>
    </channel>
</rss>