<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Elnaril</title>
    <description>The latest articles on Forem by Elnaril (@elnaril).</description>
    <link>https://forem.com/elnaril</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1096476%2Fb29212a6-438a-446a-b8cf-a211154567eb.jpg</url>
      <title>Forem: Elnaril</title>
      <link>https://forem.com/elnaril</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/elnaril"/>
    <language>en</language>
    <item>
      <title>The 🐍 SDK for the Uniswap Universal Router 🦄 is part of Hacktoberfest 2025</title>
      <dc:creator>Elnaril</dc:creator>
      <pubDate>Tue, 30 Sep 2025 14:56:52 +0000</pubDate>
      <link>https://forem.com/elnaril/the-sdk-for-the-uniswap-universal-router-is-part-of-hacktoberfest-2025-4ob1</link>
      <guid>https://forem.com/elnaril/the-sdk-for-the-uniswap-universal-router-is-part-of-hacktoberfest-2025-4ob1</guid>
      <description>&lt;p&gt;As you all probably know, &lt;strong&gt;Hacktoberfest&lt;/strong&gt; is an annual event which aims to promote open-source and having fun while growing skills, and takes place during all October.&lt;/p&gt;

&lt;p&gt;This year, the Python Codec for the &lt;strong&gt;Uniswap Universal Router&lt;/strong&gt; will participate. For that, I created and tagged &lt;a href="https://github.com/Elnaril/uniswap-universal-router-decoder/issues" rel="noopener noreferrer"&gt;several improvements/updates&lt;/a&gt;, for intermediate and advanced but also for beginner developers. Look for the &lt;code&gt;hacktoberfest&lt;/code&gt; label.&lt;/p&gt;

&lt;p&gt;To participate check the &lt;a href="https://hacktoberfest.com/participation/" rel="noopener noreferrer"&gt;Hacktoberfest website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please, share it! :)&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>blockchain</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Python Tutorial: How to rate limit async requests to credit-based APIs</title>
      <dc:creator>Elnaril</dc:creator>
      <pubDate>Sun, 20 Apr 2025 15:33:48 +0000</pubDate>
      <link>https://forem.com/elnaril/python-tutorial-how-to-rate-limit-async-requests-to-credit-based-apis-ahj</link>
      <guid>https://forem.com/elnaril/python-tutorial-how-to-rate-limit-async-requests-to-credit-based-apis-ahj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This tutorial demonstrates how to use the Python &lt;code&gt;credit-rate-limit&lt;/code&gt; library to manage API rate limits based on credits or compute unit per time unit, or cups, for asynchronous requests, illustrated with a working example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://elnaril.hashnode.dev/how-to-rate-limit-python-async-requests-to-etherscan-and-other-apis" rel="noopener noreferrer"&gt;previous tutorial&lt;/a&gt;, I introduced the Python &lt;code&gt;credit-rate-limit&lt;/code&gt; library for handling APIs with rate limits based on a number of requests per time unit, like Etherscan’s 5 requests per second.&lt;/p&gt;

&lt;p&gt;However, some API providers use &lt;strong&gt;credit-based rate limiting&lt;/strong&gt;, where each request consumes a variable number of “credits” (e.g., endpoint A: 15 credits, endpoint B: 20 credits) within a fixed allowance per time unit (e.g., 50 credits per second).&lt;/p&gt;

&lt;p&gt;As an example, we'll write &lt;code&gt;async&lt;/code&gt; requests to a Web3 RPC provider (namely &lt;a href="https://www.infura.io/" rel="noopener noreferrer"&gt;Infura&lt;/a&gt;), but that could be any other API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What will do the example script?
&lt;/h2&gt;

&lt;p&gt;For the sake of this tutorial, we want to call 2 endpoints that cost a different number of credits, with &lt;code&gt;async&lt;/code&gt; requests.&lt;br&gt;
Let's choose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eth_getBlockByNumber&lt;/code&gt;: this endpoint will provide the block for a given block number. From this block we'll take the timestamp.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eth_getUncleCountByBlockNumber&lt;/code&gt;: this endpoint will provide the number of &lt;a href="https://docs.alchemy.com/docs/what-are-uncle-blocks" rel="noopener noreferrer"&gt;uncles&lt;/a&gt; for a given block number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, the script is going to build a list of timestamped counts of uncle blocks, by scheduling on the event loop 100 requests to each of these endpoints.&lt;/p&gt;

&lt;p&gt;Uncles made sense when Ethereum was &lt;em&gt;PoW&lt;/em&gt;, so we'll look for blocks that are before &lt;a href="https://ethereum.org/en/roadmap/merge" rel="noopener noreferrer"&gt;&lt;code&gt;The Merge&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What do we need ?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The libraries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://web3py.readthedocs.io/en/stable/index.html" rel="noopener noreferrer"&gt;web3.py&lt;/a&gt;: the well known Python library for web3. Feel free to use any other library to build your request (aiohttp, httpx, ...) that suits your use case.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Elnaril/credit-rate-limit" rel="noopener noreferrer"&gt;credit-rate-limit&lt;/a&gt;: the &lt;strong&gt;Python async Rate Limiter that’s going to make our dev life suddenly easier!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: I’m the author of the &lt;code&gt;credit-rate-limit&lt;/code&gt; library. ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To install them in your virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="n"&gt;credit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Infura API
&lt;/h3&gt;

&lt;p&gt;Requesting our 2 endpoints will cost the following number of credits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eth_getBlockByNumber&lt;/code&gt; costs 80 credits per call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eth_getUncleCountByBlockNumber&lt;/code&gt; costs 150 credits per call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Infura credit allowance for the free plan is 500 credits per second.&lt;/p&gt;

&lt;p&gt;And you will need a free API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to code ?
&lt;/h2&gt;

&lt;p&gt;First, let's write the API requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncWeb3&lt;/span&gt;

&lt;span class="n"&gt;aw3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncWeb3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AsyncWeb3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncHTTPProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WEB3_HTTP_PROVIDER_URL_ETHEREUM_MAINNET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aw3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;uncle_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aw3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uncle_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added a &lt;code&gt;counter()&lt;/code&gt; method to count the number of calls and report it on the terminal while the script is executing.&lt;br&gt;
You can see its definition in the full script at the end of the tutorial.&lt;/p&gt;

&lt;p&gt;We &lt;code&gt;gather&lt;/code&gt; them so both endpoints are called for a given block number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_timestamp_and_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the &lt;code&gt;main()&lt;/code&gt; function where we schedule all 200 requests on the event loop at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;start_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;
&lt;span class="n"&gt;scanned_blocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;coros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block_number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_block&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;scanned_blocks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;coros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_timestamp_and_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;timestamps_and_uncle_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;coros&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# print(timestamps_and_uncle_counts)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamps_and_uncle_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; timestamped uncle counts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And see the full script at the end of the tutorial for the call to this &lt;code&gt;main()&lt;/code&gt; function with the execution duration.&lt;/p&gt;

&lt;h2&gt;
  
  
  First run
&lt;/h2&gt;

&lt;p&gt;❗Boom❗The script crashed! &lt;strong&gt;&lt;em&gt;'Too Many Requests'&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aiohttp.client_exceptions.ClientResponseError: 429, &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Too Many Requests'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's normal since we have not managed the rate limit yet!&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply the rate limit to our code
&lt;/h2&gt;

&lt;p&gt;So, let's do it!&lt;/p&gt;

&lt;p&gt;First, we define the credit allowance per time unit, which is 500 credits per second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;credit_rate_limit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CreditRateLimiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throughput&lt;/span&gt;
&lt;span class="n"&gt;rate_limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CreditRateLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;interval&lt;/code&gt; is in seconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_credits&lt;/code&gt; is the number of credits you can use during &lt;code&gt;interval&lt;/code&gt; seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, thanks to the &lt;code&gt;throughput&lt;/code&gt; decorator, we decorate each request with the &lt;code&gt;CreditRateLimiter&lt;/code&gt; instance we've just defined and the cost associated to the concerned endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@throughput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aw3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="nd"&gt;@throughput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;uncle_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aw3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uncle_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;request_credits&lt;/code&gt;: the cost in credits of the decorated request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rate_limiter&lt;/code&gt;: the &lt;code&gt;CreditRateLimiter&lt;/code&gt; we wish to use. We could define several of them if we had many APIs and/or providers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to re-try?&lt;/p&gt;

&lt;h2&gt;
  
  
  Second run
&lt;/h2&gt;

&lt;p&gt;✅ Success ✅&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Requests: 200/200 - 100%
Got 100 timestamped uncle counts
Duration: 69.9773 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus tip: Optimizing Your Rate Limiter for Better Performance!
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CreditRateLimiter&lt;/code&gt; comes with a handy parameter, adjustment. It’s a float that can take any value between &lt;code&gt;0&lt;/code&gt; (default) and &lt;code&gt;interval&lt;/code&gt;. There’s no right value, you just need to try and see if the requests are rejected by the API or not. Depending on your use case, this parameter can greatly improve the performances.&lt;/p&gt;

&lt;p&gt;It also means you can easily make your requests work first, and then and only if you wish, you can optimize them.&lt;/p&gt;

&lt;p&gt;For this script I could use &lt;code&gt;adjustment=0.9&lt;/code&gt; without being rate limited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CreditRateLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adjustment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a massive performance improvement: the script duration decreased by more than 50%!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Requests: 200/200 - 100%
Got 100 timestamped uncle counts
Duration: 32.1815 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;credit-rate-limit&lt;/code&gt; gets you covered as well for credit-based rate limit APIs. If you haven't done it yet, I'd recommend to check the &lt;a href="https://elnaril.hashnode.dev/how-to-rate-limit-python-async-requests-to-etherscan-and-other-apis" rel="noopener noreferrer"&gt;tutorial on rate limits based on a number of requests per time unit&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Elnaril/credit-rate-limit" rel="noopener noreferrer"&gt;Library code source and doc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/credit-rate-limit/" rel="noopener noreferrer"&gt;PyPI Package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://elnaril.hashnode.dev/how-to-rate-limit-python-async-requests-to-etherscan-and-other-apis" rel="noopener noreferrer"&gt;Other Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it, happy coding!! :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Script: Putting It All Together for Seamless Execution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;credit_rate_limit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CreditRateLimiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throughput&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncWeb3&lt;/span&gt;

&lt;span class="n"&gt;aw3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncWeb3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AsyncWeb3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncHTTPProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WEB3_HTTP_PROVIDER_URL_ETHEREUM_MAINNET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="n"&gt;start_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;
&lt;span class="n"&gt;scanned_blocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="n"&gt;rate_limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CreditRateLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adjustment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;scanned_blocks&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&amp;gt; Requests: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;scanned_blocks&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scanned_blocks&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@throughput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aw3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="nd"&gt;@throughput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate_limiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;uncle_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aw3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uncle_count&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_timestamp_and_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;get_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;coros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block_number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_block&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;scanned_blocks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;coros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_timestamp_and_uncle_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;timestamps_and_uncle_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;coros&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# print(timestamps_and_uncle_counts)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamps_and_uncle_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; timestamped uncle counts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Duration: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>api</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What Uniswap Universal Router commands are actually used?</title>
      <dc:creator>Elnaril</dc:creator>
      <pubDate>Wed, 20 Dec 2023 11:08:13 +0000</pubDate>
      <link>https://forem.com/elnaril/what-uniswap-universal-router-commands-are-actually-used-43cd</link>
      <guid>https://forem.com/elnaril/what-uniswap-universal-router-commands-are-actually-used-43cd</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;There are more than &lt;a href="https://github.com/Uniswap/universal-router#how-the-command-byte-is-structured"&gt;30 functions&lt;/a&gt; implemented in the Universal Router (UR) at the moment, in the repository main branch.&lt;/p&gt;

&lt;p&gt;As the author of the &lt;a href="https://github.com/Elnaril/uniswap-universal-router-decoder"&gt;Python Uniswap Universal Router SDK&lt;/a&gt;, I always wonder what should be the next one(s) this library should support.&lt;/p&gt;

&lt;p&gt;Until now, I've added the most obvious functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the ones that allow to swap ERC-20 tokens:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;V3_SWAP_EXACT_IN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V3_SWAP_EXACT_OUT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V2_SWAP_EXACT_IN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V2_SWAP_EXACT_OUT&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;those often needed before or after:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WRAP_ETH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNWRAP_WETH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PERMIT2_PERMIT&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;and a couple ones that allow more possibilities:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SWEEP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PAY_PORTION&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for the next supported functions, I decided to list those that are actually used, and order them from the most to the least called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;p&gt;To reach this goal, I wrote a simple Python script that looks into all blocks included in Mainnet during November 2023, gets all transactions sent to the Universal Router, discards those with a too small input field, and decodes them. And counts how many times each command (function) is used.&lt;/p&gt;

&lt;p&gt;This script can be found &lt;a href="https://gist.github.com/Elnaril/e6d51bcc43c81d5cbd9a4a494bcb2876"&gt;here&lt;/a&gt;, so the results can be reproduced. Just change the RPC providers.&lt;/p&gt;

&lt;p&gt;No fancy optimizations were implemented in the script apart from good ol' &lt;code&gt;asyncio&lt;/code&gt; and it does not check whether the transactions were rejected by the EVM or not, to avoid requesting the receipts. This is a bit annoying in terms of clean data, but allowed me to get the results in less than half a day.&lt;/p&gt;

&lt;p&gt;Another limitation with this method: it does not take into account transactions using the UR through custom contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The scripts scanned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;214 307&lt;/code&gt; blocks (from &lt;code&gt;18 473 543&lt;/code&gt; to &lt;code&gt;18 687 850&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2 453 214&lt;/code&gt; transactions sent to the UR&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;5 435 322&lt;/code&gt; commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the raw results is given in the below dict, with the decimal commands as keys and the number of calls as values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Results: {11: 1362528, 8: 1886773, 9: 97389, 1: 52226, 12: 856319, 10: 593542, 0: 494207, 5: 8346, 4: 36076, 6: 47881, 16: 25, 25: 2, 14: 8}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There were 32 transactions that could not be decoded, either because they are wrongly formatted and rejected by the EVM, or their format does not respect the specifications (as I understand them) but still were accepted by the EVM (more on them in another post). Anyway they are not statistically relevant, so let's just disregard them for now.&lt;/p&gt;

&lt;p&gt;The following table lists the results with the function names, ordered by counts (desc) and whether or not it is already supported by the codec:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dec - Hex Code&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Counts&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8 - 0x08&lt;/td&gt;
&lt;td&gt;V2_SWAP_EXACT_IN&lt;/td&gt;
&lt;td&gt;1886773&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 - 0x0b&lt;/td&gt;
&lt;td&gt;WRAP_ETH&lt;/td&gt;
&lt;td&gt;1362528&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12 - 0x0c&lt;/td&gt;
&lt;td&gt;UNWRAP_WETH&lt;/td&gt;
&lt;td&gt;856319&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 - 0x0a&lt;/td&gt;
&lt;td&gt;PERMIT2_PERMIT&lt;/td&gt;
&lt;td&gt;593542&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0 - 0x00&lt;/td&gt;
&lt;td&gt;V3_SWAP_EXACT_IN&lt;/td&gt;
&lt;td&gt;494207&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 - 0x09&lt;/td&gt;
&lt;td&gt;V2_SWAP_EXACT_OUT&lt;/td&gt;
&lt;td&gt;97389&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 - 0x01&lt;/td&gt;
&lt;td&gt;V3_SWAP_EXACT_OUT&lt;/td&gt;
&lt;td&gt;52226&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 - 0x06&lt;/td&gt;
&lt;td&gt;PAY_PORTION&lt;/td&gt;
&lt;td&gt;47881&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 - 0x04&lt;/td&gt;
&lt;td&gt;SWEEP&lt;/td&gt;
&lt;td&gt;36076&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 - 0x05&lt;/td&gt;
&lt;td&gt;TRANSFER&lt;/td&gt;
&lt;td&gt;8346&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16 - 0x10&lt;/td&gt;
&lt;td&gt;SEAPORT_V1_5&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14 - 0x0e&lt;/td&gt;
&lt;td&gt;placeholder&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25 - 0x19&lt;/td&gt;
&lt;td&gt;SUDOSWAP&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a Pie Chart:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kj4PwApP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyyzjr7r9hd07rvzpqdw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kj4PwApP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyyzjr7r9hd07rvzpqdw.png" alt="Usage Distribution of the Uniswap Universal Router Commands" width="601" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, basically, most of the used commands (99.8% of the calls) are already supported by the &lt;a href="https://github.com/Elnaril/uniswap-universal-router-decoder"&gt;Python UR SDK&lt;/a&gt;. The only one really missing is &lt;code&gt;TRANSFER&lt;/code&gt; with 8346 counts (&amp;lt; 0.2%), the others are anecdotal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SEAPORT_V1_5&lt;/code&gt; with 25 counts&lt;/li&gt;
&lt;li&gt;a placeholder with no function yet (8 counts)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SUDOSWAP&lt;/code&gt; with 2 counts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other interesting results
&lt;/h2&gt;

&lt;p&gt;1/ The 4 swap functions (&lt;code&gt;V2_SWAP_EXACT_IN&lt;/code&gt;, &lt;code&gt;V3_SWAP_EXACT_IN&lt;/code&gt;, &lt;code&gt;V2_SWAP_EXACT_OUT&lt;/code&gt; and &lt;code&gt;V3_SWAP_EXACT_OUT&lt;/code&gt;) were called &lt;code&gt;2 530 595&lt;/code&gt; times:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1 984 162&lt;/code&gt; (78.4%) for a V2 pool, most of it (74.6%) through the &lt;code&gt;V2_SWAP_EXACT_IN&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;546 433&lt;/code&gt; (21.6%) for a V3 pool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2/ The second most used function is &lt;code&gt;WRAP_ETH&lt;/code&gt; with &lt;code&gt;1 362 528&lt;/code&gt; calls, which is 53.8% of all swap commands. Knowing that several swap commands can be chained after a single &lt;code&gt;WRAP_ETH&lt;/code&gt;, this suggests that ETH is largely used to initiate a swap.&lt;/p&gt;

&lt;p&gt;3/ There are 2.2 commands/functions per transaction on average.&lt;/p&gt;

&lt;p&gt;4/ Still on average, each block contains 11.4 transactions sent to the Universal Router!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As expected, the functions related to ERC-20 DeFi are extensively used. But, basically, nothing else is used apart the &lt;code&gt;TRANSFER&lt;/code&gt; function: no batch ones, no permit2 transfers, no NFTs, ...&lt;br&gt;
So, I guess I'll implement &lt;code&gt;TRANSFER&lt;/code&gt; sooner or later, but what about the other ones?&lt;br&gt;
Do you think there is an interest in them? If, yes which ones and why are they not already used?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Python SDK for the Uniswap Universal Router - Latest Version just Released</title>
      <dc:creator>Elnaril</dc:creator>
      <pubDate>Tue, 04 Jul 2023 19:34:45 +0000</pubDate>
      <link>https://forem.com/elnaril/python-sdk-for-the-uniswap-universal-router-latest-version-just-released-2al8</link>
      <guid>https://forem.com/elnaril/python-sdk-for-the-uniswap-universal-router-latest-version-just-released-2al8</guid>
      <description>&lt;p&gt;Decode and encode easily swap transactions to the &lt;strong&gt;Uniswap Universal Router&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The latest version (v0.9.0) of this open source library has just been released and provide support for more features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Elnaril/uniswap-universal-router-decoder"&gt;Source on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/uniswap-universal-router-decoder"&gt;Packaged on PyPI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What functions of the UR would you like to see in the next release?&lt;/p&gt;

</description>
      <category>python</category>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>web3</category>
    </item>
    <item>
      <title>✨✨ Tutorial: How to Buy a Token on the 🦄 Uniswap Universal Router 🦄 with 🐍 Python 🐍 | HackerNoon ✨✨</title>
      <dc:creator>Elnaril</dc:creator>
      <pubDate>Tue, 06 Jun 2023 11:07:24 +0000</pubDate>
      <link>https://forem.com/elnaril/tutorial-how-to-buy-a-token-on-the-uniswap-universal-router-with-python-hackernoon-3icc</link>
      <guid>https://forem.com/elnaril/tutorial-how-to-buy-a-token-on-the-uniswap-universal-router-with-python-hackernoon-3icc</guid>
      <description>&lt;p&gt;Creating a swap transaction for the latest Uniswap router is not as straightforward as it used to be with the previous routers: you cannot just use the router ABI and invoke a swap function.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://hackernoon.com/how-to-buy-a-token-on-the-uniswap-universal-router-with-python"&gt;guide&lt;/a&gt; will teach you how to use Python to build the transaction input data you need to send to the Uniswap Universal Router (UR) in order to buy a token with ETH.&lt;/p&gt;

&lt;p&gt;✨✨ &lt;a href="https://hackernoon.com/how-to-buy-a-token-on-the-uniswap-universal-router-with-python"&gt;Tutorial link&lt;/a&gt; ✨✨&lt;/p&gt;

&lt;p&gt;I have developed some time ago an open source Python codec for the UR.Since then I have been asked to clarify some points about its usage. So I decided to write this tutorial.&lt;/p&gt;

&lt;p&gt;I would love to hear any constructive feedback about both the &lt;a href="https://github.com/Elnaril/uniswap-universal-router-decoder"&gt;Uniswap Universal Router codec&lt;/a&gt; and this &lt;a href="https://hackernoon.com/how-to-buy-a-token-on-the-uniswap-universal-router-with-python"&gt;tutorial&lt;/a&gt;, so feel free to comment about them!&lt;/p&gt;

&lt;p&gt;Thanks in advance! :)&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
      <category>ethereum</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
