<?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: 9xj89gzrtw-hue</title>
    <description>The latest articles on DEV Community by 9xj89gzrtw-hue (@9xj89gzrtwhue).</description>
    <link>https://dev.to/9xj89gzrtwhue</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%2F4207468%2F218fdd48-ddd6-45ce-816f-f1143dde2dc7.png</url>
      <title>DEV Community: 9xj89gzrtw-hue</title>
      <link>https://dev.to/9xj89gzrtwhue</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/9xj89gzrtwhue"/>
    <language>en</language>
    <item>
      <title>10 Production-Ready Micro-Interactions &amp; Generative SVG Badges for Web3 Landings (Zero Deps)</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Wed, 28 Oct 2026 00:09:35 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/10-production-ready-micro-interactions-generative-svg-badges-for-web3-landings-zero-deps-367g</link>
      <guid>https://dev.to/9xj89gzrtwhue/10-production-ready-micro-interactions-generative-svg-badges-for-web3-landings-zero-deps-367g</guid>
      <description>&lt;h1&gt;
  
  
  10 Production-Ready Micro-Interactions &amp;amp; Generative SVG Badges for Web3 Landings
&lt;/h1&gt;

&lt;p&gt;Most modern SaaS and Web3 landing pages look identical: generic Tailwind blur gradients, uninspired hero sections, and stock component libraries. But high-converting pages stand out through tactile &lt;strong&gt;micro-interactions&lt;/strong&gt; and crisp, &lt;strong&gt;vector-sharp generative assets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this guide, we dive into building high-fidelity interactive components using standard CSS variables, native DOM events, and standalone Python SVG generation — with zero heavyweight JavaScript framework dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Physics-Based 3D Tilt Card (Roman Numeral Holler Card)
&lt;/h2&gt;

&lt;p&gt;Rather than pulling a 50kb React spring animation library, modern browsers give us high-performance perspective transformations via CSS &lt;code&gt;transform: perspective() rotateX() rotateY()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tilt-card"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inner-glare"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;EDITION MMXXVI&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Sovereign Node&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Procedural cryptographic identity asset with dynamic ambient glare.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.tilt-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;440px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;145deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;#18181b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;#09090b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.1s&lt;/span&gt; &lt;span class="nb"&gt;ease-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.inner-glare&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;radial-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;circle&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;--mouse-x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;--mouse-y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;70%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&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="nx"&gt;e&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&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;cx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rotX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&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;rotY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`perspective(1000px) rotateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rotX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;deg) rotateY(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rotY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;deg) scale3d(1.02, 1.02, 1.02)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--mouse-x&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&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="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--mouse-y&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseleave&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;perspective(1000px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Procedural SVG Brandmark Generation in Python
&lt;/h2&gt;

&lt;p&gt;Sharp vectors load instantly (0 HTTP network overhead when inlined) and look pristine on 4K Apple Retina screens. Here is how we generate customized geometric brandmarks on the fly:&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;math&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_holler_badge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Generates a concentric neo-brutalist SVG badge with mathematical precision.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cy&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="mi"&gt;150&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;num_teeth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&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;num_teeth&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;num_teeth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lines&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;line x1=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; y1=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; x2=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; y2=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; stroke=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#10b981&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; stroke-width=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; stroke-linecap=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;round&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; /&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&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;lt;svg xmlns=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://www.w3.org/2000/svg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; viewBox=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0 0 300 300&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; width=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;300&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; height=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;300&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;
      &amp;lt;rect width=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; height=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; fill=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#09090b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; rx=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; /&amp;gt;
      &amp;lt;circle cx=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; cy=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cy&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; r=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; fill=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; stroke=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rgba(16, 185, 129, 0.2)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; stroke-width=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; /&amp;gt;
      &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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
      &amp;lt;text x=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; y=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; font-family=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monospace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; font-size=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;14&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; font-weight=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; fill=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#f4f4f5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; text-anchor=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;middle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; letter-spacing=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/text&amp;gt;
    &amp;lt;/svg&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;

&lt;span class="c1"&gt;# Example execution
&lt;/span&gt;&lt;span class="n"&gt;svg_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_holler_badge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VERIFIED_NODE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;badge.svg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svg_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. What Else is in the Production Kit?
&lt;/h2&gt;

&lt;p&gt;We assembled a full library of 10 plug-and-play UI components built without framework bloat:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Roman Numeral Holler Card&lt;/strong&gt;: 3D tilt with metallic specular highlight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neo-Brutalist Crypto Checkout Card&lt;/strong&gt;: 1-click address copy, QR generator, network switcher.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Animated Terminal Block&lt;/strong&gt;: Typewriter command replay with copy button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bento Spotlight Grid&lt;/strong&gt;: Grid cards reacting to global cursor movement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infinite Marquee Ticker&lt;/strong&gt;: CSS hardware-accelerated continuous banner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Animated Radial Token Metric&lt;/strong&gt;: SVG ring with live easing animations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Accordion FAQ&lt;/strong&gt;: Native &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; with smooth height expansion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toast Notification Engine&lt;/strong&gt;: Micro-library for alerts under 2KB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Pricing Switcher&lt;/strong&gt;: Monthly/Annual pricing calculator with animated toggle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procedural SVG Generator CLI&lt;/strong&gt;: Python script producing custom high-res assets in seconds.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Live Demo &amp;amp; Pro Pack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Interactive Showcase&lt;/strong&gt;: &lt;a href="https://9xj89gzrtw-hue.github.io/#products" rel="noopener noreferrer"&gt;9xj89gzrtw-hue.github.io/#products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Crypto / Bot Purchase&lt;/strong&gt;: Buy the full source bundle (ZIP + SVG CLI + 10 components) via Telegram &lt;a href="https://t.me/Shop010_bot" rel="noopener noreferrer"&gt;@Shop010_bot&lt;/a&gt; or instant USDC checkout on Base.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>css</category>
      <category>design</category>
    </item>
    <item>
      <title>Building an On-Chain Token Holder &amp; LP Forensics Scanner in Python (Free CLI + Pro Pack)</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Tue, 27 Oct 2026 22:45:04 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/building-an-on-chain-token-holder-lp-forensics-scanner-in-python-free-cli-pro-pack-1f9l</link>
      <guid>https://dev.to/9xj89gzrtwhue/building-an-on-chain-token-holder-lp-forensics-scanner-in-python-free-cli-pro-pack-1f9l</guid>
      <description>&lt;p&gt;Every token scanner I tried gave me a number and no way to check it. "Top 10 holders: 47%." Okay — 47% of &lt;em&gt;what&lt;/em&gt;? Of total supply? Of the supply the tool could see? Of the supply minus the pool? The answer changes the conclusion completely, and most tools never say.&lt;/p&gt;

&lt;p&gt;So I wrote a holder/LP/early-buyer scanner in Python with one rule: &lt;strong&gt;it prints the denominator.&lt;/strong&gt; If it could only see 0.33% of supply, the verdict says so on the same line as the percentage. If a pool has no fungible LP token, it says that instead of printing &lt;code&gt;0.00% burned&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This post is the design, the parts that were harder than expected, and the honest limits of doing this with public RPCs and zero API keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  Constraints I set before writing code
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Python standard library only.&lt;/strong&gt; No &lt;code&gt;pip install&lt;/code&gt;, no venv, no build step. &lt;code&gt;git clone&lt;/code&gt; and run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No API keys.&lt;/strong&gt; Public RPCs for chain data, the public DexScreener API for pools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No invented numbers.&lt;/strong&gt; When a value can't be determined, print why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No tracebacks.&lt;/strong&gt; Every network failure becomes a sentence that says what to do next.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those four constraints are what make the thing usable at 2am. A tool that requires a &lt;code&gt;.env&lt;/code&gt; with three keys is a tool you don't run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: holders from raw logs
&lt;/h2&gt;

&lt;p&gt;The core loop is embarrassingly simple and that's why it works on any EVM chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; for the token's &lt;code&gt;Transfer(address,address,uint256)&lt;/code&gt; topic over a block window.&lt;/li&gt;
&lt;li&gt;Collect every address that appears in &lt;code&gt;topics[1]&lt;/code&gt; (from) or &lt;code&gt;topics[2]&lt;/code&gt; (to).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;balanceOf&lt;/code&gt; each one in a thread pool.&lt;/li&gt;
&lt;li&gt;Compute shares, Gini, HHI, dust count.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;TRANSFER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;addresses_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&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;log&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;logs&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;topic&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;topics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;40&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;seen&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things bit me here, all of them about &lt;em&gt;limits&lt;/em&gt;, not logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunking, because &lt;code&gt;eth_getLogs&lt;/code&gt; has a ceiling
&lt;/h3&gt;

&lt;p&gt;Ask a public node for 50 000 blocks of logs and you get one of: a timeout, a &lt;code&gt;block range too large&lt;/code&gt; error, or a &lt;code&gt;query returned more than 10000 results&lt;/code&gt;. Every provider draws the line somewhere else.&lt;/p&gt;

&lt;p&gt;The fix is to shrink and retry rather than fail:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;from_block&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;to_block&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="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunk_size&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;to_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eth_getLogs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fromBlock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;toBlock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;topics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TRANSFER&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;}]))&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RpcError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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;chunk_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;
            &lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;          &lt;span class="c1"&gt;# the node is telling us something
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;start&lt;/code&gt; is only advanced on success. The naive version advances it in the &lt;code&gt;except&lt;/code&gt; too, silently dropping blocks — and then your coverage number is wrong, which is worse than crashing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrency, because 500 &lt;code&gt;balanceOf&lt;/code&gt; calls in series is 90 seconds
&lt;/h3&gt;

&lt;p&gt;An 8-worker pool with a per-call retry takes the same work down to ~6 seconds. Balances that fail after retries are counted as &lt;code&gt;balances failed&lt;/code&gt;, not as zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unique addresses in window            103
balances read                         103
balances failed                         0
holders (balance &amp;gt; 0)                  68
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That three-line block is the most important part of the output. It's the difference between "this token has 68 holders" and "I successfully read 103 of 103 balances, and 68 of them are non-zero."&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage, the number everyone hides
&lt;/h3&gt;

&lt;p&gt;Here's a real run on BRETT over a 2 000-block window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tracked supply                 32,754,149
total supply                9,999,997,544
coverage of totalSupply             0.33%
top 1 share                        48.41%
top 10 share                       99.52%

verdict: EXTREME - top-1 holds 48.4%, top-10 holds 99.5%
         - shares are of the 32,754,149 we could see (0.3% of totalSupply)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 99.5% top-10 concentration sounds like a rug. But it's 99.5% of the 0.33% of supply that moved inside my window — i.e. it's a statement about &lt;em&gt;who traded recently&lt;/em&gt;, not about the cap table. Both numbers are in the verdict line so you can't quote one without the other.&lt;/p&gt;

&lt;p&gt;This is the single design decision I'd defend hardest. If your scanner doesn't print coverage, its concentration numbers are decoration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: liquidity pools and the LP-burn question
&lt;/h2&gt;

&lt;p&gt;"LP burned" is the most cargo-culted metric in memecoin due diligence. The check is: read the LP token's &lt;code&gt;totalSupply&lt;/code&gt;, then its balance at &lt;code&gt;0x0&lt;/code&gt;, &lt;code&gt;0x…dEaD&lt;/code&gt; and at the pair contract itself.&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;BURN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x000000000000000000000000000000000000dead&lt;/span&gt;&lt;span class="sh"&gt;"&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;lp_breakdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lp_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;erc20_totalSupply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lp_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;burned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;erc20_balanceOf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lp_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&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;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BURN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;held&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;erc20_balanceOf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lp_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;burned_share&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;burned&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;floating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;burned&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;held&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;Then the part that makes this different from every dashboard: &lt;strong&gt;most modern pools don't have an LP token.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On Base, BRETT's deepest pairs are Uniswap v3 (positions are NFTs, not fungible) and a v4 singleton (the pool is a &lt;code&gt;bytes32&lt;/code&gt; id, there is no contract to call). If your scanner assumes a Uniswap-v2-shaped pair, it prints &lt;code&gt;0.00% burned&lt;/code&gt; — and that reads like "the dev can pull the liquidity", which is a completely different claim from "this pool type has no LP token at all".&lt;/p&gt;

&lt;p&gt;So the scanner walks down the pair list until it finds one that &lt;em&gt;answers &lt;code&gt;totalSupply()&lt;/code&gt; like an ERC-20&lt;/em&gt;, and then says which one it used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;note: the 5 deeper pair(s) have no fungible LP token (v3/v4/concentrated liquidity
      or a non-ERC20 pool); LP numbers below are for pair #6 (aerodrome, 0x43bb…3dba)

note: LP tokens staked in a gauge or farm are indistinguishable from floating supply
      with this method; only 0x0 / 0x...dEaD and the pair itself are counted.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That second note is the honest one: on Aerodrome, most LP sits in a gauge. A gauge-staked position is &lt;em&gt;locked&lt;/em&gt; but shows up as "floating" here, because the gauge holds it, not &lt;code&gt;0x0&lt;/code&gt;. Two different facts, one column — so the tool says which fact the column represents.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: "insiders" is a lie, so the output says "early buyers"
&lt;/h2&gt;

&lt;p&gt;The command is called &lt;code&gt;insiders&lt;/code&gt; because that's what people search for. The output is titled &lt;strong&gt;early buyers&lt;/strong&gt;, because a transfer out of a pool only proves a swap happened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Early buyers in this window: the first 5 distinct addresses that received tokens
from a pool/router/mint address.
These are early buyers, not insiders: a transfer out of a pool only proves a swap happened.

#  address        first block  balance now  % supply  status
1  0x81db…cf4d       53172524    1,600,513     0.02%  holds
2  0x3d7e…eaf3       53172618     407.2245     0.00%  holds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On an established token, "first buyers in a 2 000-block window" means &lt;em&gt;recent&lt;/em&gt; buyers. On a token that launched inside the window, they're the launch buyers. The tool prints the window so you can tell which case you're in. Renaming a metric is a feature, not pedantry: the wrong noun is how people lose money.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: the report is one HTML file
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;report.html&lt;/code&gt; is 47 KB, has inline CSS, an inline SVG bar chart, an inline concentration gauge, &lt;strong&gt;no JavaScript&lt;/strong&gt;, no web fonts, and makes zero network requests when opened. It also prints the exact command, the UTC timestamp, the block range, and the RPC endpoint that served the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;report: report.html
json:   report.json
token:  BRETT (0x532f27101965dd16442e59d40670faf5ebb142e4)
window: 53154086-53174086 (20001 blocks)
holders: 576, top10 95.58%, verdict EXTREME
size:   46.8 KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provenance line matters more than the chart. A screenshot of a number is worthless; a screenshot of a number plus the command and block range can be reproduced by anyone in 30 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Solana is a different animal
&lt;/h2&gt;

&lt;p&gt;On Solana the equivalent of &lt;code&gt;eth_getLogs&lt;/code&gt; is &lt;code&gt;getSignaturesForAddress&lt;/code&gt; + &lt;code&gt;getTransaction&lt;/code&gt;, and &lt;code&gt;getTokenLargestAccounts&lt;/code&gt; is the obvious way to get concentration. In practice, on every free public RPC I tried, &lt;code&gt;getTokenLargestAccounts&lt;/code&gt; returns &lt;strong&gt;HTTP 429&lt;/strong&gt; every time — the method is treated as an indexer call, not a node call.&lt;/p&gt;

&lt;p&gt;So the Solana path does what it can and says what it can't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getTokenSupply&lt;/code&gt; → real total supply, always available.&lt;/li&gt;
&lt;li&gt;DexScreener markets → pools, liquidity, volume, FDV.&lt;/li&gt;
&lt;li&gt;Largest accounts → attempted, and on 429 the report prints &lt;code&gt;unavailable (RPC rate limit) — no estimate substituted&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last line is the whole philosophy. The alternative — quietly falling back to a cached number from a different epoch — produces a report that looks complete and is wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it costs to run
&lt;/h2&gt;

&lt;p&gt;Nothing. The whole thing runs against public endpoints. The trade-off is throughput: a 20 000-block scan with 8 workers takes ~40 seconds on a good day and fails politely on a bad one. If you need 500 000 blocks and 50 000 holders, you need an indexer (Alchemy, QuickNode, Dune), and the code should tell you that instead of hanging.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd build next, in order
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot diffing&lt;/strong&gt; — run the scan twice, diff the holder set. New wallets with size are the interesting signal; static concentration is mostly noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pool-side holder separation&lt;/strong&gt; — right now a pool address is a "holder". Splitting "contracts/pools" from "EOAs" changes the top-10 number a lot and is a 30-line change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CEX deposit-address detection&lt;/strong&gt; — requires a labelled address set. This is the one thing you genuinely can't do with a public RPC and a weekend.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The Lite version is MIT and public: holders + concentration + the HTML report, EVM only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/9xj89gzrtw-hue/forensics-lite" rel="noopener noreferrer"&gt;https://github.com/9xj89gzrtw-hue/forensics-lite&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's ~1 100 lines of stdlib Python with 80 tests, and it ships &lt;code&gt;examples/brett-report.html&lt;/code&gt; — a real report you can open before you run anything.&lt;/p&gt;

&lt;p&gt;The Pro pack adds the &lt;code&gt;lp&lt;/code&gt; burn analysis with the v3/v4 fallback logic, the early-buyer scan, the Solana path, and the JSON+HTML dual output — &lt;strong&gt;$19 in USDC on Base&lt;/strong&gt;, delivered by an on-chain-verified checkout on a static page (no account, no email): &lt;strong&gt;&lt;a href="https://9xj89gzrtw-hue.github.io/" rel="noopener noreferrer"&gt;https://9xj89gzrtw-hue.github.io/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you only take one thing from this post: &lt;strong&gt;print your denominator.&lt;/strong&gt; Every concentration number without a coverage number is a rumour with a decimal point.&lt;/p&gt;

</description>
      <category>python</category>
      <category>crypto</category>
      <category>blockchain</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Accept USDC Payments on a Static Site (GitHub Pages) Without a Backend or Stripe KYC</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Tue, 27 Oct 2026 22:45:02 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/how-to-accept-usdc-payments-on-a-static-site-github-pages-without-a-backend-or-stripe-kyc-4a0o</link>
      <guid>https://dev.to/9xj89gzrtwhue/how-to-accept-usdc-payments-on-a-static-site-github-pages-without-a-backend-or-stripe-kyc-4a0o</guid>
      <description>&lt;p&gt;I wanted to sell three small digital products — a couple of Python CLIs and a boilerplate. Nothing exotic. But every "sell a digital product" path I found had the same wall in front of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gumroad / Lemon Squeezy / Paddle&lt;/strong&gt; — KYC, tax forms, payout in 5-7 days, 5-10% + 50¢ per sale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt; — KYC, a server, webhooks, and a "business entity" question that stops a solo dev in a country they don't like.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real backend&lt;/strong&gt; — a server to keep alive, a database of licences, a queue for retries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a $19 download sold maybe ten times a month, all of that is more infrastructure than product. So I built the smallest thing that actually works: &lt;strong&gt;a static page on GitHub Pages that verifies a Base USDC transfer in the browser, then decrypts the file locally.&lt;/strong&gt; No backend. No accounts. No KYC in the payment path.&lt;/p&gt;

&lt;p&gt;This post is the whole design, the code that matters, and — importantly — the parts where it is &lt;em&gt;not&lt;/em&gt; as strong as a real payment processor.&lt;/p&gt;




&lt;h2&gt;
  
  
  The shape of the trick
&lt;/h2&gt;

&lt;p&gt;A static page cannot receive money. But it can &lt;em&gt;read&lt;/em&gt; money that has already moved.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The buyer sends &lt;code&gt;19 USDC&lt;/code&gt; on Base to my address, from any wallet, with any wallet app.&lt;/li&gt;
&lt;li&gt;They copy the transaction hash.&lt;/li&gt;
&lt;li&gt;They paste the hash into the page.&lt;/li&gt;
&lt;li&gt;The page asks a public Base RPC for the transaction receipt and checks: &lt;em&gt;did this transaction contain a USDC &lt;code&gt;Transfer&lt;/code&gt; of at least 19.000000 USDC into my address?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;If yes, the page shows the passphrase for an AES-256 encrypted zip. The buyer runs &lt;code&gt;openssl&lt;/code&gt; (or any unzip tool) locally and gets the files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The page never holds a secret that is worth stealing, the file is public anyway (it's on GitHub Pages), and the whole "licensing" layer is a client-side string comparison. That last sentence is the honest limitation, and I'll get to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: read the receipt, not the transaction
&lt;/h2&gt;

&lt;p&gt;The naive approach is to call &lt;code&gt;eth_getTransactionByHash&lt;/code&gt; and look at &lt;code&gt;to&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;. That fails for USDC for two reasons: USDC is an ERC-20, so &lt;code&gt;value&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; is the token contract — the actual payment is a log inside the receipt.&lt;/p&gt;

&lt;p&gt;So the check is on &lt;code&gt;eth_getTransactionReceipt&lt;/code&gt;, over the logs:&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;TRANSFER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Transfer(address,address,uint256)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USDC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x833589fcd6edb6e08f4c7c32d4f71b54bda02913&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// native USDC on Base&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pad32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^0x/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sumUsdcIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&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;want&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pad32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;total&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;for &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;l&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&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="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;USDC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topics&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="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;TRANSFER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topics&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;want&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// topics[2] = `to`&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;total&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;Two details that cost me an hour each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;topics[1]&lt;/code&gt; is &lt;code&gt;from&lt;/code&gt;, &lt;code&gt;topics[2]&lt;/code&gt; is &lt;code&gt;to&lt;/code&gt;.&lt;/strong&gt; Not the other way round. If you get this backwards, every payment "fails" and you'll spend an evening debugging your RPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sum, don't take the first match.&lt;/strong&gt; Routers and smart wallets emit several &lt;code&gt;Transfer&lt;/code&gt; logs in one transaction. Summing every match into your address is both more correct and more forgiving.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Amounts are in USDC base units (6 decimals), so &lt;code&gt;19 USDC&lt;/code&gt; is &lt;code&gt;19000000n&lt;/code&gt;. Use &lt;code&gt;BigInt&lt;/code&gt; — &lt;code&gt;19_000_000&lt;/code&gt; fits in a double, but &lt;code&gt;1e18&lt;/code&gt;-style values elsewhere will silently round.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: don't trust one RPC
&lt;/h2&gt;

&lt;p&gt;Public Base RPCs rate-limit, and &lt;code&gt;mainnet.base.org&lt;/code&gt; in particular refuses &lt;code&gt;eth_getLogs&lt;/code&gt;. For receipts it mostly works, but "mostly" is not a property you want in a checkout.&lt;/p&gt;

&lt;p&gt;The page tries four endpoints in order and takes the first that answers:&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;RPCS&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="s2"&gt;https://mainnet.base.org&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="s2"&gt;https://base-rpc.publicnode.com&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="s2"&gt;https://base.llamarpc.com&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="s2"&gt;https://1rpc.io/base&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;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &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="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;RPCS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;res&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="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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&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="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&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;j&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;j&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&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;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&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="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all RPC endpoints failed&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;A receipt is final on Base long before you read it, so there's no need for confirmations logic: &lt;code&gt;status === "0x1"&lt;/code&gt; is enough. (If you want to be strict, compare &lt;code&gt;receipt.blockNumber&lt;/code&gt; against &lt;code&gt;eth_blockNumber&lt;/code&gt; and require ~30 blocks. For a $19 zip file I don't bother.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: make the hash un-reusable
&lt;/h2&gt;

&lt;p&gt;The obvious attack on a hash-based unlock is: one buyer posts their hash in a public Discord, and everyone else redeems it.&lt;/p&gt;

&lt;p&gt;The client-side fix is weak but non-zero: remember redeemed hashes in &lt;code&gt;localStorage&lt;/code&gt; and refuse a second use &lt;strong&gt;on that device&lt;/strong&gt;. It stops accidental double-spend and casual sharing of the &lt;em&gt;page state&lt;/em&gt;. It does not stop anyone with a fresh browser profile.&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;USED_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tw:redeemed:v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;usedHashes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USED_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[]&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="k"&gt;catch&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;markUsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&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;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usedHashes&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USED_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&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;If you want real single-use semantics, you need shared state — a server, or a chain contract. Which defeats the point of this design. Be honest with yourself about which one you built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: the file is encrypted, not "protected"
&lt;/h2&gt;

&lt;p&gt;The download is a zip encrypted with &lt;code&gt;openssl enc -aes-256-cbc -pbkdf2&lt;/code&gt;. The page contains the passphrase. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The archive is useless without the passphrase (good against someone who finds the raw file).&lt;/li&gt;
&lt;li&gt;The passphrase is in the page source (so it is &lt;strong&gt;not&lt;/strong&gt; a licence).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point isn't DRM. The point is that the &lt;em&gt;content&lt;/em&gt; isn't casually scrapable by a crawler, and the buyer gets a normal file they can open with normal tools. Every "protect your digital download" product sells you the same illusion with a nicer dashboard.&lt;/p&gt;

&lt;p&gt;Publishing the SHA-256 next to the passphrase is worth it — it lets the buyer confirm the archive they decrypted is the one I built:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://example.github.io/packs/kit-pro.zip.enc
openssl enc &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-aes-256-cbc&lt;/span&gt; &lt;span class="nt"&gt;-pbkdf2&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; kit-pro.zip.enc &lt;span class="nt"&gt;-out&lt;/span&gt; kit-pro.zip
shasum &lt;span class="nt"&gt;-a&lt;/span&gt; 256 kit-pro.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: test the verifier against a real mainnet transaction
&lt;/h2&gt;

&lt;p&gt;This is the step that separates "it works on my machine" from "it works". I picked a real 0.02 USDC transfer on Base and wrote tests that run the &lt;em&gt;same&lt;/em&gt; &lt;code&gt;verify.js&lt;/code&gt; in Node:&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;// 0.02 USDC to 0x7284…299c, block-confirmed&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xd27af909788e20f66ac57e2e4f49c0c610fdbf5d6ff20066669076e770945527&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepts a real 0.02 USDC transfer to the merchant&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&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;DefiAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MERCHANT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minUnits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paidUnits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20000&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejects the same tx at a higher price&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&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;DefiAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MERCHANT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minUnits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20001&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejects a tx paid to someone else&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&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;DefiAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OTHER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minUnits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&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;18 tests, all against live Base data: wrong recipient, underpayment by one base unit, a reverted transaction, a non-USDC transfer, a garbage hash, a hash from another chain. The negative cases matter more than the positive one — the positive case is the one you'll notice is broken.&lt;/p&gt;

&lt;p&gt;The same file is loaded by the browser (&lt;code&gt;window.DefiAudit&lt;/code&gt;) and by the test runner (&lt;code&gt;module.exports&lt;/code&gt;), so the code under test is the code that unlocks the page. This is the single most valuable decision in the whole project.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this design is actually good for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero fixed cost.&lt;/strong&gt; No server, no monthly fee, no account. Hosting is GitHub Pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No KYC in the payment path.&lt;/strong&gt; The buyer needs a wallet with USDC on Base; that's it. No email, no cookie banner, no "create an account to download".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant delivery.&lt;/strong&gt; A Base transaction is final in ~2 seconds. Confirmation to download is bounded by the buyer's typing speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditable.&lt;/strong&gt; Every sale is a public transaction. You can reconcile revenue against your own address with one &lt;code&gt;eth_getLogs&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refund-friendly in the honest sense.&lt;/strong&gt; No chargebacks exist, so you should say out loud that all sales are final — and price accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it is not good for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is not a licence server.&lt;/strong&gt; Anyone who reads the page can extract the passphrase. If your product is a $500 course, this is the wrong architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't stop hash sharing&lt;/strong&gt; beyond &lt;code&gt;localStorage&lt;/code&gt;. There's no way around that without shared state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual step.&lt;/strong&gt; The buyer has to copy a hash. That's one more step than a card form, and some percentage of people will bounce at it. This is a real cost, not a footnote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the support.&lt;/strong&gt; "I paid but it says invalid" is now your inbox. In practice the causes are: wrong chain (they paid on Ethereum or Solana), wrong token (USDT, or bridged USDC), or an unconfirmed tx.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No recurring billing.&lt;/strong&gt; Fine for one-time packs, useless for subscriptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The numbers I'd actually publish
&lt;/h2&gt;

&lt;p&gt;If you're deciding whether to copy this: the whole thing is ~250 lines of JavaScript, ~120 lines of CSS, and a 40-line Python script that encrypts the packs and rewrites the catalogue. It took an afternoon. The Lite version of the paywall — the verifier, the checkout UI and the tests, without the storefront copy — is public:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/9xj89gzrtw-hue/usdc-paywall-lite" rel="noopener noreferrer"&gt;https://github.com/9xj89gzrtw-hue/usdc-paywall-lite&lt;/a&gt;&lt;/strong&gt; (MIT)&lt;/p&gt;

&lt;p&gt;It includes &lt;code&gt;verify.js&lt;/code&gt;, the 18-test suite pointed at real Base transactions, and a &lt;code&gt;README&lt;/code&gt; that tells you exactly where the security model ends.&lt;/p&gt;

&lt;p&gt;If you'd rather skip the assembly, the Pro pack adds the multi-product catalogue, the per-product passphrase rotation, the SHA-256 manifest and the deploy script — &lt;strong&gt;$29 in USDC on Base, delivered by the mechanism described above&lt;/strong&gt; (which is a nice bit of dogfooding): &lt;strong&gt;&lt;a href="https://9xj89gzrtw-hue.github.io/" rel="noopener noreferrer"&gt;https://9xj89gzrtw-hue.github.io/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Either way: read the code before you trust it with money. That's the whole point of a design with no backend — you &lt;em&gt;can&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>javascript</category>
      <category>crypto</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to verify an on-chain bounty ledger down to the wei (open-source Python engine)</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Mon, 26 Oct 2026 23:28:39 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/how-to-verify-an-on-chain-bounty-ledger-down-to-the-wei-open-source-python-engine-47k5</link>
      <guid>https://dev.to/9xj89gzrtwhue/how-to-verify-an-on-chain-bounty-ledger-down-to-the-wei-open-source-python-engine-47k5</guid>
      <description>&lt;p&gt;A "who earned what" CSV request is the most under-estimated job in on-chain data. It sounds like &lt;code&gt;eth_getLogs&lt;/code&gt; plus a &lt;code&gt;groupBy&lt;/code&gt;. It is not, and the ways it breaks are specific and repeatable.&lt;/p&gt;

&lt;p&gt;I got tired of re-deriving the same five rules on every bounty, so I wrote them into an engine: &lt;a href="https://github.com/9xj89gzrtw-hue/ledgerkit" rel="noopener noreferrer"&gt;&lt;code&gt;ledgerkit&lt;/code&gt;&lt;/a&gt;, MIT, ~1k lines of Python. The design goal was not "produce a CSV". It was &lt;strong&gt;produce a CSV and a second computation that can disagree with it&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch  -&amp;gt;  build  -&amp;gt;  verify
logs       ledger     reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;eth-abi

&lt;span class="c"&gt;# 1. every log the contract ever emitted&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; ledgerkit fetch &lt;span class="nt"&gt;--chain&lt;/span&gt; arbitrum &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--address&lt;/span&gt; 0xdffe8a4a4103f968ffd61fd082d08c41dcf9b940 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-block&lt;/span&gt; 0 &lt;span class="nt"&gt;--out&lt;/span&gt; logs.json

&lt;span class="c"&gt;# 2. fold them into a per-wallet ledger&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; ledgerkit build &lt;span class="nt"&gt;--logs&lt;/span&gt; logs.json &lt;span class="nt"&gt;--fee-bps&lt;/span&gt; 250 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--csv&lt;/span&gt; wallets.csv &lt;span class="nt"&gt;--summary&lt;/span&gt; summary.json

&lt;span class="c"&gt;# 3. reconcile against an independent recount&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; ledgerkit verify &lt;span class="nt"&gt;--logs&lt;/span&gt; logs.json &lt;span class="nt"&gt;--fee-bps&lt;/span&gt; 250 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--expect-earned&lt;/span&gt; 3.726923002 &lt;span class="nt"&gt;--expect-paid&lt;/span&gt; 3.63374992695
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The middle step writes a file, so the aggregation can be audited without touching the network.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five rules that make the numbers right
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Earned is gross; paid out is net.&lt;/strong&gt; The fee is read from the settlement event, never recomputed from a percentage. Protocols change their fee rate, and old settlements keep the old one. &lt;code&gt;--fee-bps&lt;/code&gt; is a &lt;em&gt;check&lt;/em&gt;, and a mismatch is reported rather than silently applied. If a contract took 3% two years ago and 2.5% now, a single &lt;code&gt;--fee-bps 250&lt;/code&gt; pass would silently rewrite history — the engine refuses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Cancelled bounties pay nobody.&lt;/strong&gt; A settlement event on a cancelled bounty is surfaced as a warning, because it means one of the two events is wrong. Silently trusting settlements is how a CSV ends up paying someone for a bounty that was cancelled an hour before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A claim without a bounty is not guessed.&lt;/strong&gt; If the creation event falls outside the fetched block range — extremely common with a &lt;code&gt;--from-block&lt;/code&gt; set for speed — the claim is counted in &lt;code&gt;claims_accepted&lt;/code&gt; but excluded from the money columns, under &lt;code&gt;claims_valued&lt;/code&gt;, and reported. Inventing an amount is how a ledger drifts from reality in a way nobody notices until an accountant does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Duplicate &lt;code&gt;(tx, logIndex)&lt;/code&gt; pairs are dropped.&lt;/strong&gt; Retried &lt;code&gt;eth_getLogs&lt;/code&gt; pages are the single most common source of doubled totals, and public RPCs retry constantly. Deduplicating on the pair, not on the tx hash, is the only correct key: one transaction legitimately settles several claims.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Exclusions are explicit and shared.&lt;/strong&gt; &lt;code&gt;--exclude-tx 0x…&lt;/code&gt; removes known-bad settlements (exploits, test transactions) before aggregation, and the verifier applies the same list — so the builder and the checker cannot disagree about what was excluded.&lt;/p&gt;

&lt;p&gt;NFTs get their own rule: ownership is a &lt;strong&gt;last-transfer&lt;/strong&gt; problem, not a sum. Balances are rebuilt from deltas (mint &lt;code&gt;+1&lt;/code&gt;, burn &lt;code&gt;−1&lt;/code&gt;, transfer &lt;code&gt;−1/+1&lt;/code&gt;) and compared per holder.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;verify&lt;/code&gt; actually does
&lt;/h2&gt;

&lt;p&gt;Every check is a &lt;em&gt;second computation&lt;/em&gt;, not a re-run of the builder:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;check&lt;/th&gt;
&lt;th&gt;method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;conservation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;earned == paid_out + fees&lt;/code&gt;, wei-exact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;raw_recount_earned&lt;/code&gt; / &lt;code&gt;_fees&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;money columns re-derived by slicing raw &lt;code&gt;data&lt;/code&gt; words with &lt;code&gt;int(hex, 16)&lt;/code&gt;, bypassing the ABI decoder entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;raw_recount_settlements&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;settlement count from raw topics vs the ledger's &lt;code&gt;claims_valued&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;no_negative_values&lt;/code&gt;, &lt;code&gt;paid_out_within_earned&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;per-wallet sanity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no_empty_rows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;every row carries activity — no phantom wallets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fee_rate_matches&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;total fees against &lt;code&gt;--fee-bps&lt;/code&gt;, within rounding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nft_balances&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ERC-721 balances from transfer deltas, optionally against &lt;code&gt;balanceOf&lt;/code&gt; on chain&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Real output, against the golden fixture (3,467 logs from a live escrow contract):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ok   expected earned 3.726923002 ETH, got 3.726923002
ok   expected paid 3.63374992695 ETH, got 3.63374992695
ok   conservation: earned=3726923002000000000 paid_out=3633749926950000000 fees=93173075050000000 diff=0
ok   raw_recount_earned: raw=3726923002000000000 ledger=3726923002000000000
ok   raw_recount_fees: raw=93173075050000000 ledger=93173075050000000
ok   raw_recount_settlements: raw=278 ledger=278
ok   no_negative_values: 0 wallet(s) below zero
ok   paid_out_within_earned: 0 wallet(s) paid more than earned
ok   no_empty_rows: 0 wallet(s) carry no activity
ok   fee_rate_matches: expected=93173075050000000 (at 250 bps) actual=93173075050000000
ok   nft_balances: 81 holder(s) from deltas vs 81 in ledger, 722 vs 722 tokens (0 token(s) with unseen history skipped)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any mismatch exits non-zero, so it drops into CI as-is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python -m ledgerkit verify --logs logs.json --fee-bps 250 --expect-earned &lt;/span&gt;&lt;span class="m"&gt;3.726923002&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why integer arithmetic is not optional
&lt;/h2&gt;

&lt;p&gt;Every money value stays integer wei until formatting. &lt;code&gt;3.726923002 ETH&lt;/code&gt; is never a float, because &lt;code&gt;3726923002000000000&lt;/code&gt; in a float64 is &lt;code&gt;3726923002000000000.0&lt;/code&gt; right up until it is not, and the failure shows up as a one-wei disagreement between two reports that should be identical. The verifier compares integers. That is the whole trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the engine will not do for you
&lt;/h2&gt;

&lt;p&gt;It does not know your protocol. Mapping an unfamiliar contract's events — which event is a settlement, which is a cancellation, whether the fee is inside the transfer or taken before it — is the actual work, and it is per-contract. The engine gives you the invariants; the mapping is judgment.&lt;/p&gt;

&lt;p&gt;That is also why the honest version of this service is not a SaaS dashboard. It is: read the contract, write the mapping, run the ledger, hand over a reconciliation that survives a second opinion — and say out loud which rows are unvalued and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to get it
&lt;/h2&gt;

&lt;p&gt;Code, tests and the golden fixture: &lt;a href="https://github.com/9xj89gzrtw-hue/ledgerkit" rel="noopener noreferrer"&gt;github.com/9xj89gzrtw-hue/ledgerkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want the run rather than the tool — a per-wallet ledger for your escrow or bounty contract with the verification report attached — the scope is agreed in a public GitHub issue first, and it is paid in USDC on Base. Details and an example of the same approach applied to a different data problem (411 DefiLlama fee feeds silently reporting zero) are on the store page:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://9xj89gzrtw-hue.github.io/" rel="noopener noreferrer"&gt;9xj89gzrtw-hue.github.io&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Tip jar / custom data bounties
&lt;/h3&gt;

&lt;p&gt;USDC on Base, any amount, same address the store uses:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0x72Aa6edDE71B94f6b99c8e52EdC02B5C75F74d1a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Custom work I take: on-chain ledgers with reconciliation (from $149), non-standard event mappings (from $249), DefiLlama fee/revenue adapters (from $99). Open an issue with the word &lt;code&gt;quote&lt;/code&gt; and what you need — public and timestamped, no DM required.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>opensource</category>
      <category>python</category>
      <category>web3</category>
    </item>
    <item>
      <title>We audited 411 DeFi protocols reporting $0 fees on DefiLlama — $3.33M/day is missing from the data</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Mon, 26 Oct 2026 23:28:37 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/we-audited-411-defi-protocols-reporting-0-fees-on-defillama-333mday-is-missing-from-the-data-1dlm</link>
      <guid>https://dev.to/9xj89gzrtwhue/we-audited-411-defi-protocols-reporting-0-fees-on-defillama-333mday-is-missing-from-the-data-1dlm</guid>
      <description>&lt;p&gt;If your protocol has a revenue page on DefiLlama, someone has probably linked it in a deck. If that page has been showing &lt;code&gt;$0&lt;/code&gt; for a week, nobody told you.&lt;/p&gt;

&lt;p&gt;We pulled all 3,069 protocol fee listings from DefiLlama's public API on &lt;strong&gt;26 October 2026&lt;/strong&gt; and compared each one against its own history. The result is uncomfortable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;411 protocols&lt;/strong&gt; published non-zero fees at some point in the last 12 months and exactly &lt;code&gt;0&lt;/code&gt; on each of the last 7 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;80 protocols&lt;/strong&gt; on Base hold at least $300,000 of TVL and appear in &lt;em&gt;neither&lt;/em&gt; the fee overview nor the DEX-volume overview. No revenue line at all.&lt;/li&gt;
&lt;li&gt;The measured aggregate of the missing fee flow is &lt;strong&gt;$3,333,964 per day&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before you quote that last number anywhere, read the next section. It is the reason this audit is worth reading and most "we found 411 broken adapters" posts are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part where we were wrong
&lt;/h2&gt;

&lt;p&gt;We hand-checked the ten largest and most recent breaks. We ran each adapter through its own harness in &lt;code&gt;DefiLlama/dimension-adapters&lt;/code&gt;, then hit the protocol's data source directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ten out of ten were not adapter bugs.&lt;/strong&gt; The source genuinely reported zero:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;USD AI&lt;/strong&gt; (Arbitrum, $266M TVL) — no &lt;code&gt;FeePaid&lt;/code&gt; event in two days. The protocol collected nothing. The adapter was correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theo Straddle Vaults&lt;/strong&gt; (Base) — &lt;code&gt;pricePerShare&lt;/code&gt; unchanged for 96 hours. Nothing to fix.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A mass "fix 411 adapters" campaign would have opened 411 PRs against data that was already right, and would have been a public accusation against $266M protocols based on a parser artefact. That is the failure mode this space is full of.&lt;/p&gt;

&lt;p&gt;So the audit ships two numbers, and only one of them is a work queue:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;$3,333,964/day&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;aggregate measured hole across all 411 rows, including dead protocols&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;$699,504/day&lt;/strong&gt; across &lt;strong&gt;56&lt;/strong&gt; protocols&lt;/td&gt;
&lt;td&gt;same measurement, restricted to protocols that still hold ≥$300k TVL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;$23,735/day&lt;/strong&gt; across &lt;strong&gt;18&lt;/strong&gt; protocols&lt;/td&gt;
&lt;td&gt;broke in the last 30 days &lt;em&gt;and&lt;/em&gt; still holds TVL — the actionable set&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Where the breaks actually are
&lt;/h2&gt;

&lt;p&gt;Days dark, for the 411 silent feeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  0-30d   ######################################## 74
 30-90d   #################################################################### 130
 90-180d  ############################################################ 102
180-365d  ############################################################# 105
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TVL at the break tells a blunter story:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TVL band&lt;/th&gt;
&lt;th&gt;Protocols&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;under $50k&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;332&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$50k–$300k&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$300k–$1M&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$1M–$10M&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;over $10M&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most silent feeds belong to protocols with negligible TVL. They are dead listings, not broken pipelines, and treating them as a fix queue is how you waste a month. The top of the list by raw &lt;code&gt;$/day&lt;/code&gt; is almost entirely this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Chain&lt;/th&gt;
&lt;th&gt;TVL&lt;/th&gt;
&lt;th&gt;Last published&lt;/th&gt;
&lt;th&gt;Days dark&lt;/th&gt;
&lt;th&gt;Fees/day before&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avalon USDa&lt;/td&gt;
&lt;td&gt;Ethereum&lt;/td&gt;
&lt;td&gt;$3&lt;/td&gt;
&lt;td&gt;2025-11-07&lt;/td&gt;
&lt;td&gt;352&lt;/td&gt;
&lt;td&gt;$559,748&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lava&lt;/td&gt;
&lt;td&gt;Arbitrum&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;2025-12-26&lt;/td&gt;
&lt;td&gt;303&lt;/td&gt;
&lt;td&gt;$459,262&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pyth Pro&lt;/td&gt;
&lt;td&gt;Solana&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;2026-09-08&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;$291,779&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USD AI&lt;/td&gt;
&lt;td&gt;Arbitrum&lt;/td&gt;
&lt;td&gt;$266,198,319&lt;/td&gt;
&lt;td&gt;2026-10-18&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;$258,434&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kaito Capital Launchpad&lt;/td&gt;
&lt;td&gt;Base&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;2026-01-26&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;td&gt;$154,803&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Impermax V2&lt;/td&gt;
&lt;td&gt;ZKsync Era&lt;/td&gt;
&lt;td&gt;$530,455&lt;/td&gt;
&lt;td&gt;2026-01-24&lt;/td&gt;
&lt;td&gt;274&lt;/td&gt;
&lt;td&gt;$85,106&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equilibria&lt;/td&gt;
&lt;td&gt;BSC&lt;/td&gt;
&lt;td&gt;$11,214,028&lt;/td&gt;
&lt;td&gt;2026-03-25&lt;/td&gt;
&lt;td&gt;214&lt;/td&gt;
&lt;td&gt;$78,003&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two protocols with a combined TVL of $3 are at the top of a list ordered by "fees per day". That is what a naive sort gives you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The more interesting half: 80 Base protocols with no fee page
&lt;/h2&gt;

&lt;p&gt;Of 908 protocols with Base TVL, 80 have at least $300k and appear in neither overview (bridges and CEXs excluded). A protocol with TVL and no fee page gets no revenue line, no revenue ranking, and is absent from every fee-based yield comparison. For a fee-charging product, that is not a cosmetic gap.&lt;/p&gt;

&lt;p&gt;The top of that list is not small:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Base TVL&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spark Savings&lt;/td&gt;
&lt;td&gt;$1,600,605,788&lt;/td&gt;
&lt;td&gt;Yield&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anemoy Capital&lt;/td&gt;
&lt;td&gt;$329,988,170&lt;/td&gt;
&lt;td&gt;RWA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team Finance&lt;/td&gt;
&lt;td&gt;$61,639,998&lt;/td&gt;
&lt;td&gt;Token Locker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZEROBASE CeDeFi&lt;/td&gt;
&lt;td&gt;$60,795,107&lt;/td&gt;
&lt;td&gt;Basis Trading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gami Labs&lt;/td&gt;
&lt;td&gt;$58,410,645&lt;/td&gt;
&lt;td&gt;Risk Curators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brickken&lt;/td&gt;
&lt;td&gt;$42,430,974&lt;/td&gt;
&lt;td&gt;RWA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A $1.6B savings protocol with no fee adapter on the chain it is biggest on. That is either a deliberate choice or an oversight, and it is worth knowing which.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce any single row in two commands
&lt;/h2&gt;

&lt;p&gt;The whole audit is a public script, so you do not have to trust a table in a blog post.&lt;/p&gt;

&lt;p&gt;DefiLlama's own summary endpoint, for any slug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'https://api.llama.fi/summary/fees/wbtc?dataType=dailyFees'&lt;/span&gt; | jq &lt;span class="s1"&gt;'.total7d'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter harness, from a clone of &lt;code&gt;DefiLlama/dimension-adapters&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ts-node &lt;span class="nt"&gt;--transpile-only&lt;/span&gt; cli/testAdapter.ts fees/wbtc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the rule that decides everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;harness prints &lt;strong&gt;real numbers&lt;/strong&gt;, API prints zero → publish-side or chain-mapping problem, not an adapter problem;&lt;/li&gt;
&lt;li&gt;both print &lt;strong&gt;zero&lt;/strong&gt; → check the source before you write a single line. Usually the protocol simply stopped charging;&lt;/li&gt;
&lt;li&gt;harness &lt;strong&gt;throws or returns nothing&lt;/strong&gt; → now you have an adapter bug, and now a PR is justified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scripts, the raw CSVs and the evidence log are public: &lt;a href="https://github.com/9xj89gzrtw-hue/defillama-fee-feed-audit" rel="noopener noreferrer"&gt;github.com/9xj89gzrtw-hue/defillama-fee-feed-audit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own protocol
&lt;/h2&gt;

&lt;p&gt;I built a free search over all 491 rows — 411 silent feeds plus the 80 Base protocols with no adapter. Type a name or a chain; it returns status, the last day the feed published, and the measured &lt;code&gt;$/day&lt;/code&gt; that went missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://9xj89gzrtw-hue.github.io/" rel="noopener noreferrer"&gt;9xj89gzrtw-hue.github.io&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The free tier deliberately withholds the part that is work: the root-cause class, the exact adapter file, the reproduction commands, and the priority ranking. Those are in a $29 pack, paid in USDC on Base and verified on-chain. There is also a $99 tier where the patch gets written and the PR opened with harness output before and after.&lt;/p&gt;

&lt;p&gt;Two things I will not do, and you should hold me to them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No cold DMs pairing a defect with a wallet address.&lt;/strong&gt; That pattern reads as extortion, it gets accounts banned, and it is not how anyone gets paid here. The payment follows a merged, visible contribution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No promise that a maintainer merges anything.&lt;/strong&gt; The patch and the proof are what is for sale; maintainer time is not mine to sell. If the source genuinely returns zero, the job is refused and refunded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The audit is a snapshot, not a feed. Feeds recover, protocols die, and a row that was true on 26 October may be wrong today. Re-run the script before you quote a number at anyone — including mine.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tip jar / custom data bounties
&lt;/h3&gt;

&lt;p&gt;If this saved you a day of digging, the address is the same one the store uses, any amount, USDC on Base:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0x72Aa6edDE71B94f6b99c8e52EdC02B5C75F74d1a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I also take scoped data work — DefiLlama fee/revenue adapters, per-wallet on-chain ledgers with reconciliation, custom parsers with tests. Open an issue with the word &lt;code&gt;quote&lt;/code&gt; on the &lt;a href="https://github.com/9xj89gzrtw-hue/defillama-fee-feed-audit/issues/new" rel="noopener noreferrer"&gt;audit repo&lt;/a&gt; and we scope it in public, which protects both sides better than any DM.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>crypto</category>
      <category>data</category>
      <category>web3</category>
    </item>
    <item>
      <title>Two Solana micro-caps under $1M that pass every filter — and one that misses by 24 days</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Mon, 26 Oct 2026 01:30:54 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/two-solana-micro-caps-under-1m-that-pass-every-filter-and-one-that-misses-by-24-days-15la</link>
      <guid>https://dev.to/9xj89gzrtwhue/two-solana-micro-caps-under-1m-that-pass-every-filter-and-one-that-misses-by-24-days-15la</guid>
      <description>&lt;p&gt;The Mycelium Strategy connects selected Solana projects through liquidity pools: more than 84% of tradable $MYC supply already sits in pools, and the fees fund the strategy's growth and new connections. That model only works if the next connection has a real product, a live market and enough depth to pair against. I screened Solana micro-caps against the ten filters below and kept the two that pass all of them; filling a third slot would have meant lowering the bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selection filters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;On Solana.&lt;/li&gt;
&lt;li&gt;Circulating market cap below $1,000,000 at the time of research.&lt;/li&gt;
&lt;li&gt;A working product that people can use today.&lt;/li&gt;
&lt;li&gt;Not a meme token (playful branding is fine if there is a substantive product).&lt;/li&gt;
&lt;li&gt;Trading for at least 60 days.&lt;/li&gt;
&lt;li&gt;At least $10,000 of 24-hour trading volume.&lt;/li&gt;
&lt;li&gt;At least $50,000 of current DEX liquidity across verified pools, with no pool counted twice.&lt;/li&gt;
&lt;li&gt;Evidence of a product update, development activity or substantive team communication within the last 30 days.&lt;/li&gt;
&lt;li&gt;Not already in the Mycelium Strategy ($KNOTS, $PRCL, $GABLE, $KINS, $DREAMS, $GUAC, $STREAM, $ESF and others).&lt;/li&gt;
&lt;li&gt;Not $SOL, $USDC, $USDT, $MYC or $ESF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Market caps are CoinGecko; liquidity and volume are DexScreener, summed over pools and de-duplicated by pool address. Snapshot: &lt;strong&gt;26 October 2026, 01:20 UTC&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;EKOS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;perpspad&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ticker&lt;/td&gt;
&lt;td&gt;$EKOS&lt;/td&gt;
&lt;td&gt;$PERPSPAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CwubepDFJndzSKFmAMAm9u8Xx3PrizAwSq8hcGimpump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PerPsCe2SJ7Q25CN4R5TTX4fmBdmknE2hQmqCt96fHL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Website&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ekos.dev/" rel="noopener noreferrer"&gt;https://ekos.dev/&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://perpspad.fun/" rel="noopener noreferrer"&gt;https://perpspad.fun/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;&lt;a href="https://x.com/ekosproject" rel="noopener noreferrer"&gt;https://x.com/ekosproject&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://x.com/PerpsPadfun" rel="noopener noreferrer"&gt;https://x.com/PerpsPadfun&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Market cap (CoinGecko)&lt;/td&gt;
&lt;td&gt;$371,707&lt;/td&gt;
&lt;td&gt;$781,476&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24h volume (DexScreener, all pools)&lt;/td&gt;
&lt;td&gt;$100,004&lt;/td&gt;
&lt;td&gt;$30,370&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Liquidity (DexScreener, dedup)&lt;/td&gt;
&lt;td&gt;$53,398 across 2 pools&lt;/td&gt;
&lt;td&gt;$142,493 across 11 pools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Days trading&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;112&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top-10 holders, pools excluded&lt;/td&gt;
&lt;td&gt;32.17%&lt;/td&gt;
&lt;td&gt;30.38%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mint / freeze authority&lt;/td&gt;
&lt;td&gt;revoked / revoked&lt;/td&gt;
&lt;td&gt;revoked / revoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LP locked&lt;/td&gt;
&lt;td&gt;99.83% of the main pool&lt;/td&gt;
&lt;td&gt;54.22% of the main pool, 0% elsewhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last verified activity&lt;/td&gt;
&lt;td&gt;commit 26 Oct 2026&lt;/td&gt;
&lt;td&gt;X post 25 Oct 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  EKOS — a knowledge compiler for agents
&lt;/h2&gt;

&lt;p&gt;EKOS compiles enterprise knowledge for AI agents: it reads source code, SQL, ETL pipelines and git history, then writes an append-only ledger where every statement carries provenance down to file and line, queryable over MCP. Users are engineering teams sitting on large legacy codebases.&lt;/p&gt;

&lt;p&gt;Available today: the site, docs and one-line &lt;code&gt;install.sh&lt;/code&gt; from the &lt;code&gt;alexeyban/EKOS&lt;/code&gt; repository all resolve, and the codebase is Rust. Activity is unambiguous — release &lt;strong&gt;v1.1.0 on 14 October 2026&lt;/strong&gt;, commits on &lt;strong&gt;26 October at 00:11 UTC&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The token's documented job is contributor coordination: a staking pool funded with 3.5M EKOS runs 90 days from 22 October 2026, and burns have removed 33.1M of the original 1B supply. Supply on-chain is 966,366,818 and circulating equals total, so market cap equals fully diluted value. No unlocks are scheduled beyond 2.33M EKOS of founder vesting that vested but was never claimed.&lt;/p&gt;

&lt;p&gt;Concentration is the weak point. Excluding the PumpSwap pool token account &lt;code&gt;BSAx6msSrfEZzC97vfXuTG42Qx7HPUYnPxaPvzFZRWyg&lt;/code&gt; (7.357%, owned by pool &lt;code&gt;7CQHf8Gv9paKYwwsEoJ5DBY4BwDpF33cDBGK2HKCcDNW&lt;/code&gt;), the top ten hold &lt;strong&gt;32.17%&lt;/strong&gt;, and the largest unlabelled account holds 8.541% — roughly 82.5M EKOS with no label tying it to team or treasury. The token page lists 18M EKOS in Streamflow contracts (14M founder vesting plus a 4M lock to 27 July 2027), but those sizes do not appear as vesting accounts in the holder table I could read, so that lock is disclosed rather than verified. Safety is good: 99.83% of main-pool LP is locked, Rugcheck raises no flags, mint and freeze authorities are revoked. The mint is Token-2022, which some integrations handle badly.&lt;/p&gt;

&lt;p&gt;For Mycelium the natural role is &lt;strong&gt;collaboration&lt;/strong&gt;, not a pool: both sides already speak MCP, so compiling the strategy's documentation into an agent-readable ledger is a cheap pilot both communities could inspect. A MYC pair is the weaker option — $53K of liquidity is thin, and a second pool would split it.&lt;/p&gt;

&lt;h2&gt;
  
  
  perpspad — fees that fund a leveraged position
&lt;/h2&gt;

&lt;p&gt;perpspad is a launchpad where every coin's on-chain sub-wallet collects trading fees and opens a leveraged perpetual on a creator-chosen market, routed through Phoenix. Coins launch on Meteora's bonding curve and migrate to DAMM v2; creators pick from roughly 60 markets and 1x–10x leverage. Fees split 15% creator, 20% treasury, 65% launcher-chosen with at least 25% to perp collateral; realised profit divides 70% to the coin's buyback reserve, 15% creator, 15% treasury.&lt;/p&gt;

&lt;p&gt;It is live: site, docs, stats and app all resolve, and the project posted a new pairing (AMZN) on &lt;strong&gt;25 October 2026&lt;/strong&gt; after a 23 October space.&lt;/p&gt;

&lt;p&gt;Market: $781,476 on 733,803,009 tokens — circulating equals total again — with $30,370 of 24-hour volume and $142,493 of liquidity across eleven pools, the Meteora DAMM v2 pool &lt;code&gt;84uf4YpzybB4vm8RsermBFqjGxThAETpyMbp5HvkVRJQ&lt;/code&gt; holding $112,738.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The gap is the token itself.&lt;/strong&gt; Nothing on the site or in the docs says what $PERPSPAD entitles a holder to; the fee flows described belong to individual coins and the platform treasury. The link between product success and token value is undocumented, and I will not invent one. After excluding the Orca pool account &lt;code&gt;668VLAgH5ZhUVG9iqJUfSkotMc5HxtxNjTja6YLK4iQ5&lt;/code&gt; (1.790%, owned by pool &lt;code&gt;5rZzBGgQCm8nZEew5VKvyQHYWRW7QSz16Azju669YyEu&lt;/code&gt;), the top ten hold &lt;strong&gt;30.38%&lt;/strong&gt;, the largest an unlabelled 7.089%. LP locking is partial: 54.22% of the main pool ($61,760) is locked, every other pool shows zero — the deep end is protected, the shallow end is not. Mint and freeze authorities are revoked per the docs and confirmed by Rugcheck, which warns of mutable metadata.&lt;/p&gt;

&lt;p&gt;For Mycelium, &lt;strong&gt;liquidity pair&lt;/strong&gt; is the defensible role: perpspad's pitch that fees should do work is close to Mycelium's thesis, and a MYC/PERPSPAD pool would route its launchers into the strategy's liquidity. A holding thesis is not supportable until the token's claim on fee flow is published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Near-miss: Arqen ($ARQ)
&lt;/h2&gt;

&lt;p&gt;Arqen runs gasless perpetuals with USDC margin and up to 10x at perps.arqen.trade, documented at docs.arqen.trade, passing nine of ten filters: $262,903 market cap (82.0M of 100M circulating), $11,202 of 24-hour volume, $50,644 of liquidity in one Meteora DAMM v2 pool — 1.3% above the floor — and 108 days of trading. Mint &lt;code&gt;oEVufzrtcAvuefkbg2iQku9A6UbFh9f4V5kEiPARQEN&lt;/code&gt;, site &lt;a href="https://arqen.trade/" rel="noopener noreferrer"&gt;https://arqen.trade/&lt;/a&gt;, X &lt;a href="https://x.com/arqentrade" rel="noopener noreferrer"&gt;https://x.com/arqentrade&lt;/a&gt;. Mint and freeze authorities are revoked, 100% of main-pool LP is locked, top ten hold 41.02%, and Rugcheck flags mutable metadata.&lt;/p&gt;

&lt;p&gt;It fails one filter: &lt;strong&gt;activity within 30 days&lt;/strong&gt;. Its last dated communication is a pinned X post from 2 September 2026 — 54 days old, 24 days past the limit — and its Telegram has been silent since April. Until that changes, it is a product without a pulse.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would not claim
&lt;/h2&gt;

&lt;p&gt;None of this is a partnership, allocation or selection by Mycelium. Two of the three have an undocumented link between product success and token value, one has an unlabelled wallet holding 8.5% of supply, and both qualifying picks are thin enough that a MYC pair would change their market structure. Those are reasons to research further, not to allocate.&lt;/p&gt;

&lt;p&gt;Discuss these findings and the Mycelium Strategy: &lt;a href="https://discord.gg/5M7FsQcGq" rel="noopener noreferrer"&gt;https://discord.gg/5M7FsQcGq&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI-created for a @myceliumfinance bounty.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mycelium — &lt;a href="https://www.myceliumfinance.com" rel="noopener noreferrer"&gt;https://www.myceliumfinance.com&lt;/a&gt;, &lt;a href="https://www.myceliumfinance.com/allocations" rel="noopener noreferrer"&gt;https://www.myceliumfinance.com/allocations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;EKOS — &lt;a href="https://ekos.dev/" rel="noopener noreferrer"&gt;https://ekos.dev/&lt;/a&gt;, &lt;a href="https://ekos.dev/token/" rel="noopener noreferrer"&gt;https://ekos.dev/token/&lt;/a&gt;, &lt;a href="https://docs.ekos.dev/docs" rel="noopener noreferrer"&gt;https://docs.ekos.dev/docs&lt;/a&gt;, &lt;a href="https://github.com/alexeyban/EKOS" rel="noopener noreferrer"&gt;https://github.com/alexeyban/EKOS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;EKOS market — &lt;a href="https://www.coingecko.com/en/coins/ekos" rel="noopener noreferrer"&gt;https://www.coingecko.com/en/coins/ekos&lt;/a&gt;, &lt;a href="https://dexscreener.com/solana/7CQHf8Gv9paKYwwsEoJ5DBY4BwDpF33cDBGK2HKCcDNW" rel="noopener noreferrer"&gt;https://dexscreener.com/solana/7CQHf8Gv9paKYwwsEoJ5DBY4BwDpF33cDBGK2HKCcDNW&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;EKOS safety — &lt;a href="https://rugcheck.xyz/tokens/CwubepDFJndzSKFmAMAm9u8Xx3PrizAwSq8hcGimpump" rel="noopener noreferrer"&gt;https://rugcheck.xyz/tokens/CwubepDFJndzSKFmAMAm9u8Xx3PrizAwSq8hcGimpump&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;perpspad — &lt;a href="https://perpspad.fun/" rel="noopener noreferrer"&gt;https://perpspad.fun/&lt;/a&gt;, &lt;a href="https://perpspad.fun/docs" rel="noopener noreferrer"&gt;https://perpspad.fun/docs&lt;/a&gt;, &lt;a href="https://perpspad.fun/stats" rel="noopener noreferrer"&gt;https://perpspad.fun/stats&lt;/a&gt;, &lt;a href="https://app.perpspad.fun/" rel="noopener noreferrer"&gt;https://app.perpspad.fun/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;perpspad market — &lt;a href="https://www.coingecko.com/en/coins/perpspad" rel="noopener noreferrer"&gt;https://www.coingecko.com/en/coins/perpspad&lt;/a&gt;, &lt;a href="https://dexscreener.com/solana/84uf4YpzybB4vm8RsermBFqjGxThAETpyMbp5HvkVRJQ" rel="noopener noreferrer"&gt;https://dexscreener.com/solana/84uf4YpzybB4vm8RsermBFqjGxThAETpyMbp5HvkVRJQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;perpspad safety — &lt;a href="https://rugcheck.xyz/tokens/PerPsCe2SJ7Q25CN4R5TTX4fmBdmknE2hQmqCt96fHL" rel="noopener noreferrer"&gt;https://rugcheck.xyz/tokens/PerPsCe2SJ7Q25CN4R5TTX4fmBdmknE2hQmqCt96fHL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Arqen — &lt;a href="https://arqen.trade/" rel="noopener noreferrer"&gt;https://arqen.trade/&lt;/a&gt;, &lt;a href="https://perps.arqen.trade/" rel="noopener noreferrer"&gt;https://perps.arqen.trade/&lt;/a&gt;, &lt;a href="https://docs.arqen.trade/" rel="noopener noreferrer"&gt;https://docs.arqen.trade/&lt;/a&gt;, &lt;a href="https://x.com/arqentrade" rel="noopener noreferrer"&gt;https://x.com/arqentrade&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Arqen market — &lt;a href="https://dexscreener.com/solana/59cbVFRS9GSYeMPVrNQtDyzGnaN8o3fyWZcPJxFuNZjD" rel="noopener noreferrer"&gt;https://dexscreener.com/solana/59cbVFRS9GSYeMPVrNQtDyzGnaN8o3fyWZcPJxFuNZjD&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Arqen safety — &lt;a href="https://rugcheck.xyz/tokens/oEVufzrtcAvuefkbg2iQku9A6UbFh9f4V5kEiPARQEN" rel="noopener noreferrer"&gt;https://rugcheck.xyz/tokens/oEVufzrtcAvuefkbg2iQku9A6UbFh9f4V5kEiPARQEN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Supply and token-account owners verified against Solana RPC (&lt;code&gt;getTokenSupply&lt;/code&gt;, &lt;code&gt;getTokenAccountsByOwner&lt;/code&gt;, &lt;code&gt;getMultipleAccounts&lt;/code&gt;), 26 October 2026&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>startup</category>
      <category>ai</category>
    </item>
    <item>
      <title>Kintara ($KINS): a Solana MMO that already runs — what's live, what's not published</title>
      <dc:creator>9xj89gzrtw-hue</dc:creator>
      <pubDate>Mon, 26 Oct 2026 00:55:44 +0000</pubDate>
      <link>https://dev.to/9xj89gzrtwhue/kintara-kins-a-solana-mmo-that-already-runs-whats-live-whats-not-published-1ba1</link>
      <guid>https://dev.to/9xj89gzrtwhue/kintara-kins-a-solana-mmo-that-already-runs-whats-live-whats-not-published-1ba1</guid>
      <description>&lt;p&gt;Kintara is a browser-based, isometric MMO that runs on Solana, and it has one quality that is rare in this sector: you can open it and play in the next thirty seconds. No download, no launcher, no password — you connect a Solana wallet and you are in.&lt;/p&gt;

&lt;p&gt;That makes it a useful case study, because the interesting question about a small Solana game is never "does the token exist", it is "how much of this is actually shipped". Here is the public record as of 26 October 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is live today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The game shell at &lt;a href="https://kintara.com/play" rel="noopener noreferrer"&gt;kintara.com/play&lt;/a&gt; responds, and the &lt;a href="https://kintara.com/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; describe shipped systems: a bank, a marketplace with two listing types (gold, and gold for $KINS), guilds, daily quests, a death "tombstone", a spin wheel, fishing and cooking.&lt;/li&gt;
&lt;li&gt;The public stats endpoint reported &lt;strong&gt;495 players online against 69,685 total accounts&lt;/strong&gt; at 03:30 UTC on 26 October 2026 (&lt;a href="https://kintara.com/api/site/stats" rel="noopener noreferrer"&gt;api/site/stats&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thirty-three public patch notes between 13 May and 14 September 2026&lt;/strong&gt; (&lt;a href="https://kintara.com/api/site/feed" rel="noopener noreferrer"&gt;api/site/feed&lt;/a&gt;), including v5.9 (free to play to level 10, email/Google/Discord/X login, marketplace rework, referral payouts in $KINS) and v6.0 (Relicweaver, minigames hub, five casino games, Season 1).&lt;/li&gt;
&lt;li&gt;Trading on 20 pairs across PumpSwap, Meteora, Raydium and Orca, the oldest created 22 May 2026 (&lt;a href="https://dexscreener.com/solana/F42tZnKPavq1VUcrL6ymhc6YqVpt84fWwgzbNTv2wb3W" rel="noopener noreferrer"&gt;DexScreener&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The audience is specific: RuneScape-shaped progression in a browser tab, with a wallet instead of a subscription. It is not trying to be a graphics showcase.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is not published
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There is no public roadmap.&lt;/strong&gt; The docs are detailed about systems and silent about dates; they say features "may be added in future updates" and state that "parties and teleport-to-friend are not live yet".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token distribution is not published either.&lt;/strong&gt; No allocation table, no vesting schedule, no treasury address list. Supply is verifiable on-chain — &lt;strong&gt;991,692,960.717712 KINS&lt;/strong&gt;, 6 decimals — but who holds it is not, and public RPCs refused the largest-accounts call during this research.&lt;/p&gt;

&lt;p&gt;The patch feed also stops at 14 September, 42 days old, after 33 entries at intervals of one to ten days. The site bundle is stamped &lt;code&gt;?v=20261023-kcperks&lt;/code&gt;, so work continues; it just stopped being documented in the public feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token utility, as documented
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Holding 1,000 $KINS&lt;/strong&gt; lifts the free-to-play level cap and opens four paid realms; holding for 24 hours unlocks trading, marketplace selling, guild-bank deposits and the death tombstone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The paid spin&lt;/strong&gt; costs $5 in $KINS at the live rate, &lt;strong&gt;50% burned, 50% to the treasury&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selling in-game gold for $KINS&lt;/strong&gt; pays the seller &lt;strong&gt;95%&lt;/strong&gt;, with 5% to the treasury.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the whole documented utility. Beyond it: no allocation, no emissions schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mycelium connection
&lt;/h2&gt;

&lt;p&gt;Kintara is one of the projects in the Mycelium Strategy. The &lt;a href="https://www.myceliumfinance.com/allocations" rel="noopener noreferrer"&gt;allocations page&lt;/a&gt; currently shows &lt;strong&gt;$KINS at 9.87% of a $32,009 live allocation ($3,160.84)&lt;/strong&gt;, in a single &lt;strong&gt;MYC/KINS Raydium pool&lt;/strong&gt; (&lt;code&gt;FmyAWpcEGTrV7hHdZ391kKycektEBZzoetL6GtjDkLua&lt;/code&gt;). DexScreener reports about &lt;strong&gt;$3,161&lt;/strong&gt; of liquidity in that pool — the two sources agree to the dollar. It is a small, verifiable link rather than a headline partnership.&lt;/p&gt;

&lt;h2&gt;
  
  
  One promising aspect
&lt;/h2&gt;

&lt;p&gt;The product shipped before the market did. The oldest trading pair dates from 22 May 2026, and there were already weeks of patch notes behind it. Thirty-three documented releases, a free-to-play funnel that removes the wallet as an onboarding wall, and 495 concurrent players on a quiet Monday in October is not a token with a landing page — it is a small game with a token attached. That ordering is much harder to fake than volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  One meaningful risk
&lt;/h2&gt;

&lt;p&gt;The access gate is economically almost decorative. Holding 1,000 $KINS costs roughly &lt;strong&gt;$0.38&lt;/strong&gt; at the current price of about $0.000383. A barrier that cheap cannot create persistent demand to hold, so the documented utility is closer to a membership than to a sink. Combine that with an unpublished distribution and unpublished treasury addresses, and the float and concentration cannot be assessed from public sources at all. That is not proof of a problem — it is the absence of the evidence you would want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to look next
&lt;/h2&gt;

&lt;p&gt;Read the feed for cadence, call the stats endpoint for live concurrency, pull the mint on Solana for supply and revoked authorities, and read the allocations page for the pool. Each of those is a fact. Roadmap, distribution and float are currently silence.&lt;/p&gt;

&lt;p&gt;Discuss these findings and the Mycelium Strategy: &lt;a href="https://discord.gg/5M7FsQcGq" rel="noopener noreferrer"&gt;https://discord.gg/5M7FsQcGq&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI-created for a Mycelium Finance bounty.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kintara site and docs — &lt;a href="https://kintara.com/" rel="noopener noreferrer"&gt;https://kintara.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Update feed — &lt;a href="https://kintara.com/api/site/feed" rel="noopener noreferrer"&gt;https://kintara.com/api/site/feed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live stats — &lt;a href="https://kintara.com/api/site/stats" rel="noopener noreferrer"&gt;https://kintara.com/api/site/stats&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Market data — &lt;a href="https://dexscreener.com/solana/F42tZnKPavq1VUcrL6ymhc6YqVpt84fWwgzbNTv2wb3W" rel="noopener noreferrer"&gt;https://dexscreener.com/solana/F42tZnKPavq1VUcrL6ymhc6YqVpt84fWwgzbNTv2wb3W&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Mycelium allocations — &lt;a href="https://www.myceliumfinance.com/allocations" rel="noopener noreferrer"&gt;https://www.myceliumfinance.com/allocations&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solanagaminweb3g</category>
    </item>
  </channel>
</rss>
