<?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>DEV Community: Superwavvy</title>
    <description>The latest articles on DEV Community by Superwavvy (@superwavvy).</description>
    <link>https://dev.to/superwavvy</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4103285%2F6be90a57-a6c2-4e2f-83ad-a3188e43f513.jpg</url>
      <title>DEV Community: Superwavvy</title>
      <link>https://dev.to/superwavvy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/superwavvy"/>
    <language>en</language>
    <item>
      <title>Stop Getting Distracted and Missing Your 2x's!</title>
      <dc:creator>Superwavvy</dc:creator>
      <pubDate>Fri, 25 Sep 2026 15:12:32 +0000</pubDate>
      <link>https://dev.to/superwavvy/stop-getting-distracted-and-missing-your-2xs-4dkc</link>
      <guid>https://dev.to/superwavvy/stop-getting-distracted-and-missing-your-2xs-4dkc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fugfc281t6vrmm266pbid.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fugfc281t6vrmm266pbid.jpg" alt=" " width="" height=""&gt;&lt;/a&gt;&lt;br&gt;
Let’s be honest. We’ve all been there. You spend hours scanning DexScreener, finally ape into a low-cap Solana gem, and then you look away for 10 minutes to do something else. You come back, and it's already up 300% without you. Or worse, it rugged while you were gone.&lt;/p&gt;

&lt;p&gt;I got tired of losing money because I couldn’t stare at a chart 24/7. So, I built a solution for me and my friends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing the Multiples Alert Bot.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s a simple Telegram bot that does one thing perfectly: it watches your tokens and yells at you when they hit your targets. No more constant refreshing. No more missed pumps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is what makes it different:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;· &lt;strong&gt;Market Cap Focused:&lt;/strong&gt; Price means nothing in memecoins. We track Market Cap (MC). If you paste a CA at a $100k MC, you get alerted at $200k, $500k, etc.&lt;br&gt;
· &lt;strong&gt;Custom Milestones:&lt;/strong&gt; By default, it tracks 2x. But with a tap of a button, you can set it to 5x, 10x, or punch in a custom sequence like 3x, 6x, 9x.&lt;br&gt;
· &lt;strong&gt;Whipsaw Protection:&lt;/strong&gt; Memecoins bounce. If your token hits 2x and drops back down, the bot won't spam you. It only resets the alert if it drops significantly (10% below the target).&lt;br&gt;
· &lt;strong&gt;The 0.5x Safety Net:&lt;/strong&gt; If the token dumps to half of your entry MC, it sends you one single "Get out now" alert. No spam, just the cold hard truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use it:&lt;/strong&gt;&lt;br&gt;
You literally just paste a Solana Contract Address (CA) into the chat. The bot scans it, logs the base MC, and starts checking the price every 15 seconds. You can track tickers too, and manage all your tokens with a simple /list command.&lt;/p&gt;

&lt;p&gt;I’m currently opening up beta access to a small group of 3-10 people to stress-test it before I deploy it to a dedicated server.&lt;/p&gt;

&lt;p&gt;If you’re tired of missing your multiples and want to give it a spin, drop a comment or DM me on Twitter @&lt;a href="https://x.com/ytnwavvy" rel="noopener noreferrer"&gt;ytnwavvy&lt;/a&gt;. Let’s catch some pumps. &lt;/p&gt;

</description>
      <category>solana</category>
      <category>memecoin</category>
      <category>python</category>
      <category>trading</category>
    </item>
    <item>
      <title>Building a CEX/DEX Alert Bot on Android: When Your Code Works, but Your Network Doesn't</title>
      <dc:creator>Superwavvy</dc:creator>
      <pubDate>Tue, 22 Sep 2026 15:54:23 +0000</pubDate>
      <link>https://dev.to/superwavvy/building-a-cexdex-alert-bot-on-android-when-your-code-works-but-your-network-doesnt-5aib</link>
      <guid>https://dev.to/superwavvy/building-a-cexdex-alert-bot-on-android-when-your-code-works-but-your-network-doesnt-5aib</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2hrxz34iacmbzarepw7z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2hrxz34iacmbzarepw7z.jpg" alt="Termux terminal screenshot showing " width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;I have a confession: my mobile network almost made me quit this project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the past week, I've been building a suite of DeFi monitoring tools that run entirely on my Android phone using Termux. The latest addition is a Price Alert Bot that monitors Centralized Exchange (CEX) prices against Decentralized Exchange (DEX) prices on Arbitrum, and watches for stablecoin depegs.&lt;/p&gt;

&lt;p&gt;It runs 24/7 in the background, sends alerts to my Telegram, and caught a real 0.60% spread on AAVE just yesterday.&lt;/p&gt;

&lt;p&gt;But getting here was a war. I dealt with Ethers v6 breaking changes, "ghost pools" showing fake 262% spreads, and a mobile ISP that actively blocks crypto APIs.&lt;/p&gt;

&lt;p&gt;Here is the story of how I built it, and the exact fixes for the errors that almost broke me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Ethers v6 Trap (And the RPC Mix-up)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're migrating from Ethers v5 to v6, brace yourself. The first error I hit was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot read properties of undefined (reading 'JsonRpcProvider')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In v6, the providers namespace was removed. Your syntax changes from new ethers.providers.JsonRpcProvider() to new ethers.JsonRpcProvider().&lt;/p&gt;

&lt;p&gt;But the real facepalm moment? I was using an Ethereum Mainnet Alchemy RPC URL for a bot querying Arbitrum contracts. The provider was timing out trying to find contracts that didn't exist on the wrong chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Always create a dedicated Arbitrum app in your Alchemy dashboard, and hardcode the chain ID to prevent mobile timeouts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ARBITRUM_RPC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42161&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arbitrum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;2. The Case of the 262% "Ghost" Spread&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I first built the price comparison logic, I used Uniswap V2 and SushiSwap. My bot immediately alerted me to a 262% arbitrage opportunity for WETH.&lt;/p&gt;

&lt;p&gt;It was a trap. Uniswap V2 and SushiSwap are ghost towns on Arbitrum. The pools are practically empty, which completely distorts the math.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Before calculating a price, query the pool's liquidity directly. If it's below a certain threshold (e.g., $50k), skip it entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pairContract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pairAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PAIR_ABI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reserves&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pairContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReserves&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Check if reserves are sufficient before proceeding&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;3. The Camelot V3 Conundrum&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once I switched to Uniswap V3 and Camelot V3, I hit a wall with Camelot. It's built on the Algebra protocol, meaning standard Uniswap V3 ABIs fail.&lt;/p&gt;

&lt;p&gt;Uniswap V3 uses getPool(tokenA, tokenB, fee). Camelot uses poolByPair(tokenA, tokenB) with no fee parameter. If you don't account for this, your quotes will revert with execution reverted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use the correct factory ABI, and remember to compare BigInts correctly (0n vs 0).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. The Mobile ISP Boss Fight&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the part that almost broke me. My bot was perfect, but it kept throwing these errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CEX Error: getaddrinfo ENOTFOUND api.bybit.com
JsonRpcProvider failed to detect network and cannot start up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My mobile carrier was actively blocking specific crypto exchange APIs and timing out the RPC connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Cloudflare WARP (1.1.1.1). I turned it on, and instantly, every ENOTFOUND error vanished. The bot was finally reaching Bybit and Uniswap without any issues.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;5. The Final Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bot now does three things every 2 hours:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Price Targets:&lt;/strong&gt; Alerts me if UNI or AAVE crosses a specific USD threshold.&lt;br&gt;
&lt;strong&gt;2. CEX/DEX Spread:&lt;/strong&gt; Compares Bybit/Coinbase prices against Uniswap V3 on Arbitrum. If the spread is &amp;gt;0.5%, it pings my Telegram.&lt;br&gt;
&lt;strong&gt;3. Depeg Monitoring:&lt;/strong&gt; Checks USDT and DAI against USDC on Arbitrum. If they deviate by &amp;gt;0.5%, I get an emergency alert.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building DeFi tools on a mobile phone is chaotic, but it's the best way to learn about real-world infrastructure. You learn to handle rate limits, ISP blocks, and AMM math quirks that you'd never encounter in a sandboxed environment.&lt;/p&gt;

&lt;p&gt;The entire codebase is open source. You can check it out on GitHub: &lt;a href="//github.com/superwavvy/price-alert-bot"&gt;github.com/superwavvy/price-alert-bot&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; I'm tackling the execution layer.&lt;/p&gt;

&lt;p&gt;Have you ever run a bot on mobile? How do you handle ISP blocks? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;Also check my previous articles, thanks!&lt;br&gt;
&lt;a href="https://dev.to/superwavvy/building-a-defi-monitoring-system-2-bots-real-data-real-insights-4ce5"&gt;Click here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>android</category>
      <category>defi</category>
      <category>web3</category>
    </item>
    <item>
      <title>Building a Multi-DEX Arbitrage Scanner on Arbitrum: When APIs Fail, Query the Blockchain</title>
      <dc:creator>Superwavvy</dc:creator>
      <pubDate>Wed, 16 Sep 2026 08:07:17 +0000</pubDate>
      <link>https://dev.to/superwavvy/building-a-multi-dex-arbitrage-scanner-on-arbitrum-when-apis-fail-query-the-blockchain-33ib</link>
      <guid>https://dev.to/superwavvy/building-a-multi-dex-arbitrage-scanner-on-arbitrum-when-apis-fail-query-the-blockchain-33ib</guid>
      <description>&lt;p&gt;I decided to challenge myself, so I built two DeFi monitoring bots that worked flawlessly. Then I tried to add a third: an arbitrage detector then I  hit a wall so hard I almost gave up.&lt;/p&gt;

&lt;p&gt;This is the story of what went wrong, why it went wrong, and how querying smart contracts directly solved every problem that free APIs created.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plan (That Didn't Work)
&lt;/h2&gt;

&lt;p&gt;Simple idea: monitor prices on Uniswap and Sushiswap, find discrepancies, alert on profit opportunities.&lt;/p&gt;

&lt;p&gt;I started with what seemed logical: use the 1inch API. Free tier, no keys needed, returns aggregate prices across all DEXs in one call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPrices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenAddresses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.1inch.com/price/v1.1/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CHAIN_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokenAddresses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat, right? One line of logic. One API call. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Except it wasn't.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 1: The 402 That Wouldn't Stop
&lt;/h2&gt;

&lt;p&gt;First test run: &lt;code&gt;API error: timeout of 5000ms exceeded&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Second test run: &lt;code&gt;API error: Request failed with status code 402 (Payment Required)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Third run: Same 402.&lt;/p&gt;

&lt;p&gt;The 1inch API free tier was rejecting my requests. Either I was rate-limited or the endpoint didn't support the way I was querying it. Either way, the API was unreliable for what I needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Free APIs have hard limits that don't care about your timeline. When they hit that limit, you're blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 2: The Alchemy RPC Timeout
&lt;/h2&gt;

&lt;p&gt;Okay, skip 1inch. I then used Alchemy's RPC directly with ethers.js. Queried the contracts myself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://arb-mainnet.g.alchemy.com/v2/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALCHEMY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat concept. Direct blockchain access. No middleman API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Except Alchemy's RPC to Arbitrum kept timing out.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;30 seconds of waiting. Nothing. Then: &lt;code&gt;JsonRpcProvider failed to detect network and cannot start up&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I had the right Alchemy key, the right RPC endpoint, everything looked correct. But Alchemy's Arbitrum endpoint was flaky from Termux on my phone's internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Even paid providers have reliability issues when you're querying from unconventional environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: The Wrong Chain
&lt;/h2&gt;

&lt;p&gt;After hours of debugging, I realized something: &lt;strong&gt;my .env file had an Alchemy key for Ethereum Mainnet, not Arbitrum.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was trying to query Arbitrum with an Ethereum endpoint. No wonder it was timing out.&lt;/p&gt;

&lt;p&gt;After hours of debugging, I caught it then hardcoded the Arbitrum chain ID and moved on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHAIN_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42161&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Arbitrum&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://arb-mainnet.g.alchemy.com/v2/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ARBITRUM_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Simple mistakes kill hours. Always verify your configuration matches your target chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 4: The Phantom Arbitrage (Ghost Pools)
&lt;/h2&gt;

&lt;p&gt;Data started flowing. I saw spreads like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uniswap V3: 1 WETH = $2269 USDC&lt;/li&gt;
&lt;li&gt;Sushiswap: 1 WETH = $626 USDC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 262% difference. Free money, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrong.&lt;/strong&gt; Sushiswap doesn't have real liquidity on Arbitrum. It's a ghost town. The contract exists, but there's no actual trading happening. The "price" the contract returned was meaningless.&lt;/p&gt;

&lt;p&gt;This is called a phantom arbitrage — a spread that looks profitable until you realize one side of the trade has zero liquidity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Before calculating profit, check if the pool actually has liquidity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniswapLiquidity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factoryContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;TOKEN_A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TOKEN_B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FEE_TIER&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// If liquidity is 0, skip this pair&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uniswapLiquidity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;liquidity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dead pool, skipping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Prices without liquidity are hallucinations. You need both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 5: The Uniswap V3 Quoter Address Mismatch
&lt;/h2&gt;

&lt;p&gt;Uniswap V3 has a &lt;code&gt;QuoterV2&lt;/code&gt; contract. It gives you exact prices based on trade size.&lt;/p&gt;

&lt;p&gt;I was using the Ethereum Mainnet QuoterV2 address on Arbitrum. Different chain, different address, different contract. It failed silently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG - Ethereum address&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QUOTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0x61fFE014bA17989E8aBf2F2B629bDA3dB2e02C00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// RIGHT - Arbitrum address&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QUOTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0x61ffE014ba17989E8aBf2F2b629BdA3Db2e02C00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point: &lt;strong&gt;Chain-specific addresses matter.&lt;/strong&gt; Copy-pasting from Ethereum docs broke everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Use Uniswap's official contract addresses for each chain, or query the factory directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 6: Camelot V3 Uses Different ABIs
&lt;/h2&gt;

&lt;p&gt;Uniswap V3 works one way. Camelot (another DEX on Arbitrum) uses the same Uniswap v3-compatible interface but with slightly different function signatures.&lt;/p&gt;

&lt;p&gt;My Uniswap V3 code crashed on Camelot because the ABI expected different return values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Uniswap V3 QuoterV2&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;quoter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quoteExactInputSingle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;tokenIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tokenOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amountIn&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Camelot V3 (Algebra protocol)&lt;/span&gt;
&lt;span class="c1"&gt;// Different function name, different return structure&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;quoter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quoteExactInputSingle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;tokenIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tokenOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amountIn&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Returns 4 values, not 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Even "compatible" protocols have subtle differences. You need to test against each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Query Contracts Directly
&lt;/h2&gt;

&lt;p&gt;After all this, I realised something simple: &lt;strong&gt;forget APIs. Query the DEX contracts directly.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://arb1.arbitrum.io:8545&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Get Uniswap V3 factory&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factoryAbi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function getPool(address,address,uint24) view returns (address)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0x1F98431c8aD98523631AE4a59f267346ea3113F8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;factoryAbi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Query the pool directly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;poolAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;WETH_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;USDC_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FEE_3000&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Check if pool exists and has liquidity&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;poolAbi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function liquidity() view returns (uint128)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;poolAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;poolAbi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;liquidity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;liquidity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;liquidity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No liquidity, skip&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API rate limits. No timeouts. No phantom spreads. Just blockchain data.&lt;/p&gt;

&lt;p&gt;The cost? You have to understand smart contract ABIs and know which addresses to query. But once you know the contracts, it's rock solid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trade-off
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;APIs are convenient until they're not.&lt;/strong&gt; They ease complexity, handle rate limiting, and return clean JSON. But they fail at inconvenient times, have hard limits, and cost money at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direct contract queries are slower to set up but more reliable once working.&lt;/strong&gt; You talk to the source of truth (the blockchain) instead of a middleman's interpretation of it.&lt;/p&gt;

&lt;p&gt;For a bot running 24/7 on my phone with limited bandwidth, direct queries won.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Running Now
&lt;/h2&gt;

&lt;p&gt;The arbitrage detector queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uniswap V3 (checks liquidity + gets quotes)&lt;/li&gt;
&lt;li&gt;Camelot V3 (different ABI, but same idea)&lt;/li&gt;
&lt;li&gt;Filters out dead pools&lt;/li&gt;
&lt;li&gt;Calculates spread after fees&lt;/li&gt;
&lt;li&gt;Alerts only on &amp;gt;0.3% spreads (to account for gas)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkArbitrage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wethPool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPoolLiquidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WETH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;USDC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uniswap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wethPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;liquidity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wethPool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;camelotPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPoolLiquidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WETH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;USDC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camelot&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;uniPrice&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;camelotPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;camelotPrice&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="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Alert: profitable opportunity&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Spread: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spread&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% | Buy on Camelot, sell on Uniswap`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running on Arbitrum. No API complaints. No timeouts. Real data every 5 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;Building production bots taught me something that tutorials don't: &lt;strong&gt;constraints are features.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being limited to free APIs, running from a phone on mobile data, and targeting an L2 that most devs ignore forced me to think about reliability instead of convenience.&lt;/p&gt;

&lt;p&gt;The code is messier because it accounts for multiple DEX ABIs, liquidity checks, and contract-specific quirks. But it's more honest about what the blockchain actually looks like.&lt;/p&gt;

&lt;p&gt;APIs smooth over these details. Sometimes that's helpful. But when the bot needs to run 24/7, you need to know the real shape of the thing you're monitoring.&lt;/p&gt;

&lt;p&gt;That's why I query contracts directly now.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Running on: Arbitrum, Ethers.js v6, Alchemy RPC (Arbitrum), Termux on Android, real data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Current spreads: Uniswap V3 vs Camelot V3 on WETH/USDC. Threshold: 0.3% (after fees).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next: 30 days of data collection and pattern analysis, while I try to incorporate either telegram/discord notifications into the codebase to alert for arbitrage opportunities.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>automation</category>
      <category>arbitrum</category>
      <category>arbitrage</category>
    </item>
    <item>
      <title>5 Errors I Hit Building DeFi Bots (And How to Fix Them)</title>
      <dc:creator>Superwavvy</dc:creator>
      <pubDate>Sat, 12 Sep 2026 08:00:31 +0000</pubDate>
      <link>https://dev.to/superwavvy/5-errors-i-hit-building-defi-bots-and-how-to-fix-them-106f</link>
      <guid>https://dev.to/superwavvy/5-errors-i-hit-building-defi-bots-and-how-to-fix-them-106f</guid>
      <description>&lt;p&gt;Building production bots on free APIs sounds cool in theory. In practice, you hit walls immediately. Here are the 5 real errors I encountered while building two Ethereum monitoring bots, and exactly how I fixed them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error 1: Alchemy Event Listeners Timeout After 5 Minutes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
I started monitoring Aave liquidations using ethers.js event listeners on free-tier Alchemy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Liquidation detected:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worked perfectly for 5 minutes. Then the WebSocket died. No error message. Just silence. Listener stopped working forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;
Free-tier Alchemy (and most providers) have timeout limits on WebSocket connections. Idle connections get dropped. No reconnect logic on the free tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Stop using event listeners. Use polling instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pollLiquidations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentBlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AAVE_LENDING_POOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;topics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LIQUIDATION_CALL_TOPIC&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;fromBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBlock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;toBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBlock&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleLiquidation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Poll every 15 minutes&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollLiquidations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; On free tier, reliability &amp;gt; real-time. Polling every 15 minutes that works beats real-time listeners that disconnect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error 2: CoinGecko Rate Limit 429 (Too Many Requests)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
I wanted to track 5 tokens: UNI, AAVE, USDC, DAI, USDT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AAVE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DAI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPrices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="nx"&gt;axios&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="s2"&gt;`https://api.coingecko.com/api/v3/simple/price?ids=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;...`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Check every 15 minutes&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getPrices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a day, I got slammed with 429 errors. CoinGecko rate-limited me. Price alerts stopped working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;
Free-tier CoinGecko allows ~5 requests per second, but daily limits kick in after 50 concurrent calls per minute. 5 tokens × 96 checks/day = 480 calls. You hit the limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Reduce frequency OR reduce tokens. I chose frequency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AAVE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 2 tokens only&lt;/span&gt;

&lt;span class="c1"&gt;// Check every 2 hours (not 15 mins)&lt;/span&gt;
&lt;span class="c1"&gt;// 2 tokens × 12 checks/day = 24 calls. Under limit.&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getPrices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still catches all meaningful moves (5%+ happens in hours, not minutes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; APIs are precious on free tier. Reduce scope, not reliability. 2 tokens checked reliably &amp;gt; 5 tokens checked intermittently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error 3: Supabase RLS "New Row Violates Row-Level Security Policy"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
Bots ran fine for 2 days. Then suddenly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Supabase error: new row violates row-level security policy for table "price_history"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both bots stopped writing data. Data collection halted. Silent failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;
I had Row-Level Security (RLS) enabled but forgot to disable it for anonymous (public) access. When the bots tried to INSERT with anon key, RLS rejected them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This would fail&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;price_history&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.94&lt;/span&gt; &lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Disable RLS on the tables (if you're the only user):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;price_history&lt;/span&gt; &lt;span class="n"&gt;DISABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;liquidations&lt;/span&gt; &lt;span class="n"&gt;DISABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or enable RLS but create a policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"Enable insert for all"&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;price_history&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; RLS is security theater on hobby projects. Disable it for free tier. Enable it when you have real users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error 4: "Cannot Find Module" In Termux File Creation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
I created a data analysis script but it wouldn't run:&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="nv"&gt;$ &lt;/span&gt;node data-analysis.js

Error: Cannot find module &lt;span class="s1"&gt;'/data/data/com.termux/files/home/data-analysis.js'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File seemed to exist, but Node couldn't find it. Turned out the file never actually created properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;
When copying files to Termux , file paths get weird. The tool thinks it created the file, but Termux didn't actually receive it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Create files directly in Termux using nano:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano data-analysis.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste content, save with Ctrl+X → Y → Enter.&lt;/p&gt;

&lt;p&gt;Verify with:&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="nb"&gt;ls&lt;/span&gt; ~/&lt;span class="k"&gt;*&lt;/span&gt;.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Don't rely on external file creation tools for Termux. Nano is slower but reliable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error 5: Can't Exit tmux On Mobile Keyboard
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
I attach to a tmux session to check bot output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux attach &lt;span class="nt"&gt;-t&lt;/span&gt; price-alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the output, want to exit. Try Ctrl+B then D. Nothing happens. Keyboard doesn't register the key combo properly on mobile.&lt;/p&gt;

&lt;p&gt;Stuck in the session. No escape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;
Mobile on-screen keyboards don't register modifier key combos well (Ctrl+B, Alt+X, etc). Long-press + tap doesn't work reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Don't attach at all. Use capture-pane instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux capture-pane &lt;span class="nt"&gt;-t&lt;/span&gt; price-alert &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows last output without entering the session. No exit needed. No stuck state.&lt;/p&gt;

&lt;p&gt;For killing a session if you DO get stuck:&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="c"&gt;# In a new terminal&lt;/span&gt;
tmux kill-session &lt;span class="nt"&gt;-t&lt;/span&gt; price-alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Avoid interactive sessions on mobile. Use read-only commands instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;All 5 errors follow the same theme:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free tier + unconventional setup (mobile, no servers) = constraints.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The fix isn't "pay more" or "use better tools." It's &lt;strong&gt;work within constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event listeners die → use polling&lt;/li&gt;
&lt;li&gt;Rate limits hit → reduce scope&lt;/li&gt;
&lt;li&gt;RLS blocks → disable it&lt;/li&gt;
&lt;li&gt;File creation fails → use native tools&lt;/li&gt;
&lt;li&gt;Keyboard combos don't work → avoid them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the real lesson. Not the specific fixes, but the &lt;strong&gt;mindset:&lt;/strong&gt; I'm not building for ideal conditions. I'm building for free APIs, free databases, and a phone running Linux.&lt;/p&gt;

&lt;p&gt;Ship anyway.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Running on: Termux (Android), free-tier Alchemy, free-tier CoinGecko, Supabase free tier, tmux&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Timeline: Sept 1-12, 2026. Two weeks into production.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>automation</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>Building a DeFi Monitoring System: 2 Bots, Real Data, Real Insights</title>
      <dc:creator>Superwavvy</dc:creator>
      <pubDate>Thu, 10 Sep 2026 09:54:22 +0000</pubDate>
      <link>https://dev.to/superwavvy/building-a-defi-monitoring-system-2-bots-real-data-real-insights-4ce5</link>
      <guid>https://dev.to/superwavvy/building-a-defi-monitoring-system-2-bots-real-data-real-insights-4ce5</guid>
      <description>&lt;p&gt;I started building on-chain tools in September with a simple goal: learn blockchain development while creating something useful. Two weeks later, I had two production bots collecting real Ethereum data. Here's what I built, why, and what the data tells us.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;DeFi moves fast. Token prices swing 5-10% in hours. Liquidations happen constantly. If you're monitoring these manually, you're always behind. I wanted to automate the watching and get instant alerts.&lt;/p&gt;

&lt;p&gt;But more importantly, I wanted to &lt;em&gt;understand&lt;/em&gt; how it works by building it myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Two complementary bots:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Price Alert Bot&lt;/strong&gt; — Monitors token prices every 2 hours, sends Telegram alerts on 5%+ moves&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Liquidation Detector&lt;/strong&gt; — Polls Aave every 15 minutes, sends Discord alerts when liquidations happen&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both feed into &lt;strong&gt;Supabase PostgreSQL&lt;/strong&gt; for persistence and analysis.&lt;/p&gt;

&lt;p&gt;Everything runs 24/7 on Termux (Node.js on my Android phone) in tmux sessions. No cloud servers. No DevOps overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Price Alert Bot
&lt;/h3&gt;

&lt;p&gt;The concept is simple: fetch prices, compare to history, alert on meaningful moves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPriceFromCoingecko&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadFromSupabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;changePercent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;lastPrice&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="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changePercent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendTelegramAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`📈 UNI moved &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;changePercent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The harder part: avoiding API limits. CoinGecko free tier is strict. I learned that checking every 15 minutes (96 calls/day per token) hits limits fast. Solution: check every 2 hours instead (12 calls/day per token). Still catches all meaningful moves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Liquidation Detector
&lt;/h3&gt;

&lt;p&gt;Liquidations are blockchain events. When someone's collateral drops below their debt threshold, Aave liquidates them. The event fires on-chain instantly.&lt;/p&gt;

&lt;p&gt;I tried event listeners first (like traditional blockchain monitoring). Free-tier Alchemy killed the connection after 5 minutes with timeouts.&lt;/p&gt;

&lt;p&gt;Switched to polling instead: query the last 9 blocks every 15 minutes (under Alchemy's 10-block free-tier limit). More reliable, same result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AAVE_LENDING_POOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;topics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LIQUIDATION_CALL_TOPIC&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;fromBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBlock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;toBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBlock&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Parse event data, store to Supabase, send Discord alert&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real Data: 2 Weeks In
&lt;/h2&gt;

&lt;p&gt;Running both bots for 2 weeks gives us actual insights:&lt;/p&gt;

&lt;h3&gt;
  
  
  Price Analysis
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;UNI (74 price records):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Range: $5.68 - $7.20&lt;/li&gt;
&lt;li&gt;Volatility: 26.76%&lt;/li&gt;
&lt;li&gt;Change: +2.21%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AAVE (18 records):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Range: $124.48 - $129.57&lt;/li&gt;
&lt;li&gt;Volatility: 4.09%&lt;/li&gt;
&lt;li&gt;Change: -3.53%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; UNI is way more volatile than AAVE. If you're building a risk model, token volatility matters. A 5% alert threshold for UNI catches real moves. Same threshold for AAVE would fire constantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Liquidation Analysis
&lt;/h3&gt;

&lt;p&gt;Liquidations detected: 0 (so far)&lt;/p&gt;

&lt;p&gt;This might sound like a failure. It's not. Aave is well-capitalized. Most positions are healthy. Liquidations spike during crashes (March 2020, May 2022). Right now, markets are calm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the data tells us:&lt;/strong&gt; The detector is working correctly. It's querying the right contract, decoding events properly, storing to Supabase. When crashes come, we'll have real-time visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Lessons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Supabase?
&lt;/h3&gt;

&lt;p&gt;Serverless PostgreSQL. Free tier gets 500MB + unlimited read/write to a point. Perfect for side projects. Both bots share one project with separate tables.&lt;/p&gt;

&lt;p&gt;Deduplication is built in: &lt;code&gt;tx_hash UNIQUE&lt;/code&gt; prevents storing the same liquidation twice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Polling Over Events?
&lt;/h3&gt;

&lt;p&gt;Event listeners sound better (real-time vs. 15-minute latency). But free-tier providers are unreliable. Polling every 15 minutes is more stable than event listeners that disconnect every 5 minutes.&lt;/p&gt;

&lt;p&gt;The tradeoff: real-time vs. reliability. I chose reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running on Your Phone
&lt;/h3&gt;

&lt;p&gt;My laptop stays off. My phone runs 24/7. Termux lets me run Node.js on Android like a normal Linux environment. I use tmux to keep both bots alive in background sessions that survive disconnects and phone restarts.&lt;/p&gt;

&lt;p&gt;To start the bots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux new-session &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; price-alert &lt;span class="s2"&gt;"node price-alert-bot.js"&lt;/span&gt;
tmux new-session &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; liquidation &lt;span class="s2"&gt;"node liquidation-detector.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check they're running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux list-sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View live output anytime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux attach &lt;span class="nt"&gt;-t&lt;/span&gt; price-alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Exit with Ctrl+B then D — doesn't kill the process)&lt;/p&gt;

&lt;p&gt;That's it. Both bots run in the background forever. My phone is now a DeFi monitoring station.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source
&lt;/h2&gt;

&lt;p&gt;Both bots are on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Price Alert Bot: github.com/superwavvy/price-alert-bot&lt;/li&gt;
&lt;li&gt;Liquidation Detector: github.com/superwavvy/liquidation-detector&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No monetization yet. Just sharing what works.&lt;/p&gt;

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

&lt;p&gt;DeFi moves fast. Building fast is how you keep up. I built two monitoring bots in 2 weeks, deployed them to production (my phone), collected real data, and learned more about blockchain than any course could teach.&lt;/p&gt;

&lt;p&gt;The data is boring right now (0 liquidations, 2% price movement). But boring data on a working system beats exciting theories on a broken one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Running on: Termux (Android), Node.js v26, Supabase, Alchemy API (free tier), CoinGecko API (free tier)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tools: ethers.js v6, axios, node-cron, tmux&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Timeline: Sept 1 - Sept 10, 2026. Full time learning, part-time earning.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>javascript</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Building a DeFi Price Alert Bot: What I Learned Building a Production System on My Phone</title>
      <dc:creator>Superwavvy</dc:creator>
      <pubDate>Wed, 02 Sep 2026 20:33:16 +0000</pubDate>
      <link>https://dev.to/superwavvy/building-a-defi-price-alert-bot-what-i-learned-building-a-production-system-on-my-phone-54j0</link>
      <guid>https://dev.to/superwavvy/building-a-defi-price-alert-bot-what-i-learned-building-a-production-system-on-my-phone-54j0</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: I built a price monitoring bot for Ethereum tokens that runs on my phone and costs $0/month. Monitors any token, sends instant Telegram alerts, logs everything to Supabase. Here's what I learned about DeFi automation, databases, and building real systems under constraints.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You're a trader. You watch 5 altcoins. Market moves 20% while you sleep.&lt;/p&gt;

&lt;p&gt;You miss it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Existing solutions suck&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TradingView: $10-100/month&lt;/li&gt;
&lt;li&gt;Binance alerts: Lag, limited tokens&lt;/li&gt;
&lt;li&gt;Manual monitoring: Burnout guaranteed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What if you built your own?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's this project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Decisions (The Foundation)
&lt;/h2&gt;

&lt;p&gt;Before coding, I made 8 key decisions:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Price Check Frequency: 15 Minutes&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Every 1-min check for 100 tokens = 144k API calls/day (too much)&lt;/li&gt;
&lt;li&gt;15 min = 192 calls/day (well within free tier)&lt;/li&gt;
&lt;li&gt;Sufficient to catch real moves, not micro-movements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Data Source: CoinGecko + Alchemy&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CoinGecko: Free price feeds, no auth needed&lt;/li&gt;
&lt;li&gt;Alchemy: Setup for future on-chain reads (liquidity, swaps, etc.)&lt;/li&gt;
&lt;li&gt;Fallback strategy built in&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Storage: Supabase PostgreSQL&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Why not Firebase or local JSON?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL = powerful queries later&lt;/li&gt;
&lt;li&gt;Managed PostgreSQL = no server ops&lt;/li&gt;
&lt;li&gt;Free tier: 500MB + generous API&lt;/li&gt;
&lt;li&gt;RLS (Row Level Security) for safety&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Token Management: JSON Config&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Simple, human-editable, version-controllable. Can add tokens without redeploying.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Triggers: Both % Changes + Price Targets&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;% changes&lt;/strong&gt; catch momentum (alert if ±5%, ±10%, ±25%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price targets&lt;/strong&gt; catch ranges (buy @ $X, sell @ $Y)&lt;/li&gt;
&lt;li&gt;Having both = flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Alerts: Telegram&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Instant push notifications (can't miss)&lt;/li&gt;
&lt;li&gt;Works on any device&lt;/li&gt;
&lt;li&gt;Rich formatting support&lt;/li&gt;
&lt;li&gt;Free and reliable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. &lt;strong&gt;Error Handling: Alert on Critical Failures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Don't fail silently. Don't spam every error. Alert when &amp;gt;50% of checks fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. &lt;strong&gt;Deployment: Termux on Phone&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zero server costs&lt;/li&gt;
&lt;li&gt;Always online (phone on charger)&lt;/li&gt;
&lt;li&gt;Can deploy and code from same device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Spend 30 mins deciding architecture. Saves hours of rework.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Node.js&lt;/strong&gt; - Event-driven runtime, Termux support&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;node-cron&lt;/strong&gt; - Runs your function every 15 minutes without a server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*/15 * * * *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runCheck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CoinGecko API&lt;/strong&gt; - Zero auth, instant price feeds&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;vs_currencies=usd`&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt; - Managed PostgreSQL with instant backups&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why PostgreSQL? SQL means flexible queries later&lt;/li&gt;
&lt;li&gt;Why managed? You're paying for not running ops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Telegram Bot API&lt;/strong&gt; - Push notifications&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple HTTP calls&lt;/li&gt;
&lt;li&gt;Instant delivery&lt;/li&gt;
&lt;li&gt;Formatted messages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How It Works (Every 15 Minutes)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Scheduler wakes up
2. Load token config (JSON)
3. For each token:
   → Fetch current price from CoinGecko
   → Query last stored price from Supabase
   → Calculate % change
   → Check triggers (% change + targets)
   → If triggered → Send Telegram alert
   → Log to database
4. Wait 15 minutes
5. Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Trigger Logic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Type 1: Percentage Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"percentageChanges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Price moved 5%+ → Alert&lt;/li&gt;
&lt;li&gt;Useful for: Catching momentum, volatility spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Type 2: Price Targets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"priceTargets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sell"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"buy"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Price crossed $5 selling level → Alert&lt;/li&gt;
&lt;li&gt;Useful for: Range trading, DCA strategies, support/resistance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Previous price: $4.50&lt;/li&gt;
&lt;li&gt;Current price: $5.10&lt;/li&gt;
&lt;li&gt;Buy target: $5.00&lt;/li&gt;
&lt;li&gt;Result: ✅ Alert "Crossed $5.00 buy level"&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Errors I Hit
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Error 1: RLS Permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permission denied for table price_history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Grant anonymous API key permissions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price_history&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price_history_id_seq&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Security-first defaults are good, but require understanding.&lt;/p&gt;




&lt;h3&gt;
  
  
  Error 2: Telegram Connection Timeout
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Telegram API wasn't responding during startup&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Made Telegram optional&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TELEGRAM_BOT_TOKEN&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;TELEGRAM_CHAT_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPriceAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;📝 [Mock Alert]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: External dependencies should fail gracefully.&lt;/p&gt;




&lt;h3&gt;
  
  
  Error 3: CoinGecko Rate Limiting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: HTTP 429 Too Many Requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Respect rate limits. Added delays between calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Test with real APIs under real constraints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance After 24 Hours
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ Price checks: 96 (every 15 min)
✅ Alerts triggered: 3
✅ Price snapshots logged: 96
✅ Errors: 0
✅ Memory used: 5-10 MB
✅ CPU during check: &amp;lt;5%
✅ CPU idle: 0%
✅ Termux stability: Perfect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This tells me&lt;/strong&gt;: System is reliable, minimal resource usage, scales to many more tokens.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Start with E2E tests&lt;/strong&gt;&lt;br&gt;
I built everything before testing end-to-end. Caught permission errors late.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Mock external APIs during development&lt;/strong&gt;&lt;br&gt;
Calling CoinGecko 50 times during dev = wasted API calls&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use TypeScript&lt;/strong&gt;&lt;br&gt;
JavaScript is fast, but TypeScript catches type errors before runtime&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Add logging levels&lt;/strong&gt;&lt;br&gt;
Current code logs everything equally. Need filtering (debug/info/error)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Document while building&lt;/strong&gt;&lt;br&gt;
Docs after = forgotten context. Docs while = fresh thinking&lt;/p&gt;


&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Phase 2: Liquidation Detector
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Monitor collateral prices on Aave, Compound&lt;/li&gt;
&lt;li&gt;Alert when liquidation price approaches&lt;/li&gt;
&lt;li&gt;Rank by urgency&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Phase 3: Arbitrage Spotter
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Compare prices across DEXs&lt;/li&gt;
&lt;li&gt;Detect profitable spreads&lt;/li&gt;
&lt;li&gt;Calculate slippage&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Architecture Matters
&lt;/h3&gt;

&lt;p&gt;30 mins of architecture decisions saved hours of rework.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Pick Managed Services
&lt;/h3&gt;

&lt;p&gt;Supabase, Alchemy, CoinGecko, Telegram = all managed. I don't run anything.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Fail Gracefully
&lt;/h3&gt;

&lt;p&gt;One error shouldn't break the whole system.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Data is Everything
&lt;/h3&gt;

&lt;p&gt;Storing every price check means I can analyze later.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Deploy Early
&lt;/h3&gt;

&lt;p&gt;Deploy to production ASAP. Production teaches you fast.&lt;/p&gt;


&lt;h2&gt;
  
  
  Code Structure
&lt;/h2&gt;

&lt;p&gt;All modular and focused:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot.js          (100 lines) - Scheduler + orchestration
db.js           (80 lines)  - Database I/O
triggers.js     (60 lines)  - Price evaluation logic
notifications.js (50 lines) - Telegram sending
config.json     (20 lines)  - Token watchlist
schema.sql      (40 lines)  - Database setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Total: ~350 lines of code.&lt;/strong&gt; You can understand the entire system in 30 mins.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is This Production-Ready?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For solo/small trading&lt;/strong&gt;: Yes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs 24/7 on phone&lt;/li&gt;
&lt;li&gt;Zero downtime&lt;/li&gt;
&lt;li&gt;Costs $0/month&lt;/li&gt;
&lt;li&gt;Scales to ~50 tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For institutional&lt;/strong&gt;: Not yet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No redundancy&lt;/li&gt;
&lt;li&gt;No disaster recovery&lt;/li&gt;
&lt;li&gt;No audit logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For learning&lt;/strong&gt;: Absolutely. This is the foundation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;The hardest part of building isn't coding. It's deciding &lt;strong&gt;what&lt;/strong&gt; to build and &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I spent 20% of time on architecture. That decision paid for itself 5x over.&lt;/p&gt;

&lt;p&gt;Next time you build, do the same:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define the problem&lt;/li&gt;
&lt;li&gt;Lock architecture&lt;/li&gt;
&lt;li&gt;Build the skeleton&lt;/li&gt;
&lt;li&gt;Deploy early&lt;/li&gt;
&lt;li&gt;Iterate based on reality&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't overthink. Build. Learn. Repeat.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Built by&lt;/strong&gt;: Wavvy&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Timeline&lt;/strong&gt;: Sept 1, 2026 (1 day start-to-production)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Live and monitoring UNI + AAVE  &lt;/p&gt;

</description>
      <category>automation</category>
      <category>web3</category>
      <category>node</category>
      <category>ethereum</category>
    </item>
  </channel>
</rss>
