<?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: Huzaifa Zahoor</title>
    <description>The latest articles on DEV Community by Huzaifa Zahoor (@nutshellcrypto).</description>
    <link>https://dev.to/nutshellcrypto</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%2F4163162%2F09d88fb0-c7d9-452c-b03b-b70d66cbc907.png</url>
      <title>DEV Community: Huzaifa Zahoor</title>
      <link>https://dev.to/nutshellcrypto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nutshellcrypto"/>
    <language>en</language>
    <item>
      <title>Is That Canadian Crypto Exchange Actually Regulated? A Developer's Verification Checklist (Plus the $30K Limit in Python)</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Mon, 26 Oct 2026 08:44:10 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/is-that-canadian-crypto-exchange-actually-regulated-a-developers-verification-checklist-plus-the-1c8a</link>
      <guid>https://dev.to/nutshellcrypto/is-that-canadian-crypto-exchange-actually-regulated-a-developers-verification-checklist-plus-the-1c8a</guid>
      <description>&lt;p&gt;If you live in Canada and use a crypto app, there's a good chance its website says "licensed" or "compliant" somewhere near the footer. Those words can mean very different things. Some platforms are registered with securities regulators and supervised on how they hold client assets. Others only have an anti-money-laundering registration, which says nothing about custody.&lt;/p&gt;

&lt;p&gt;As developers we already know this pattern: a badge in a README isn't the same as a passing test. This post treats "is this exchange regulated?" as a verification problem, and ends with a small Python model of the $30,000 purchase limit that registered Canadian platforms enforce.&lt;/p&gt;

&lt;p&gt;For the full, up-to-date list of registered platforms and what each label means, see this guide to &lt;a href="https://nutshellcrypto.com/regulated-crypto-exchanges-canada/" rel="noopener noreferrer"&gt;regulated crypto exchanges in Canada and how to check one&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two registrations that look alike but aren't
&lt;/h2&gt;

&lt;p&gt;Canada has no single national securities regulator. Each province runs its own, and they coordinate through the Canadian Securities Administrators (CSA). When a platform holds your coins and you only hold a claim on them, regulators treat that as a securities matter, so the platform has to register as a dealer.&lt;/p&gt;

&lt;p&gt;There's a second, separate registration with FINTRAC, Canada's financial intelligence agency, as a money services business. That one is about anti-money-laundering checks and KYC. It doesn't mean anyone is reviewing how customer coins are stored.&lt;/p&gt;

&lt;p&gt;So "registered" is ambiguous until you know which registry. If you're building anything that routes users or funds to an exchange, that distinction belongs in your design notes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The status labels, decoded
&lt;/h2&gt;

&lt;p&gt;On provincial lists you'll mostly see two categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restricted dealer&lt;/strong&gt;: an interim category. The platform can only do what its specific terms and conditions allow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investment dealer&lt;/strong&gt;: the same category traditional brokerages use, with membership in CIRO, the national self-regulatory body, and its capital, audit and conduct rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since 2024 the direction of travel has been for crypto platforms to move toward investment dealer status. Both labels mean a regulator has looked at the business. Neither means your crypto is insured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification as a checklist, not a vibe
&lt;/h2&gt;

&lt;p&gt;Here's the process, written the way I'd write a runbook:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resolve the legal entity.&lt;/strong&gt; The brand name isn't what gets registered. Look in the terms of service for the operating company.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query the source of truth.&lt;/strong&gt; Search that entity on the CSA's list of authorized crypto trading platforms or its National Registration Search, then check your own province's page. Never trust a "regulated" badge on the company's own site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for province scope.&lt;/strong&gt; Registration is granted province by province. A platform can be fine in Ontario and absent elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search the warnings lists.&lt;/strong&gt; Regulators publish investor alerts about unregistered firms. A match there is a hard stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diff the offering against the rules.&lt;/strong&gt; If an app offers high-leverage derivatives to Canadian retail users, that's a strong signal it isn't on the registered list.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lists also drift. The NutshellCrypto guide points out a platform shown as an investment dealer on one provincial page and a restricted dealer on another. Treat any cached copy as stale and re-check the live page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modeling the $30,000 limit in Python
&lt;/h2&gt;

&lt;p&gt;Registered platforms apply an investment limit for many retail clients: a net maximum of $30,000 CAD over 12 months on coins outside a "specified" set. Bitcoin, Ether, Bitcoin Cash, Litecoin and approved stablecoins are excluded, and sales count back toward your room. Provinces and platforms apply it a bit differently, so treat this as a simplified model, not your platform's exact formula.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;

&lt;span class="n"&gt;LIMIT_CAD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30_000&lt;/span&gt;
&lt;span class="n"&gt;EXEMPT&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;BTC&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;ETH&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;BCH&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;LTC&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;USDC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# check your platform's own list
&lt;/span&gt;
&lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="c1"&gt;# (date, asset, side, CAD amount)
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&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;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SOL&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;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BTC&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;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# exempt, doesn't count
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINK&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;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SOL&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;sell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# sales free up room
&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;remaining_room&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;today&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;today&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;365&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cad&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trades&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;asset&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;EXEMPT&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&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;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;cad&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cad&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LIMIT_CAD&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;net&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;remaining_room&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;  &lt;span class="c1"&gt;# 7000
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;remaining_room&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2027&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;15&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;   &lt;span class="c1"&gt;# SOL buy aged out -&amp;gt; 19000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is the rolling window: room comes back as old buys age past 12 months. If you're reconciling your own exports, this also explains why a purchase that was blocked last month might go through today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What registration doesn't cover
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Crypto held at a member firm isn't eligible for CIPF coverage (cash and crypto funds can be, the coins themselves can't).&lt;/li&gt;
&lt;li&gt;Deposit insurance doesn't cover crypto either.&lt;/li&gt;
&lt;li&gt;No regulator stops a coin from dropping 50%, and a transfer to the wrong address usually can't be reversed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why many people keep only what they actively trade on any exchange and move long-term holdings to a wallet they control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;"Licensed" is meaningless until you know which registry it refers to.&lt;/li&gt;
&lt;li&gt;Verify the legal entity on the CSA and provincial lists, not the company's site.&lt;/li&gt;
&lt;li&gt;Registration is per province, and lists change often.&lt;/li&gt;
&lt;li&gt;The $30,000 limit is a rolling 12-month net figure on non-specified coins.&lt;/li&gt;
&lt;li&gt;Registration reduces some risks but doesn't insure your crypto.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/regulated-crypto-exchanges-canada/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is educational content, not financial or legal advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What Failed Crypto Exchanges Teach Developers About Custody Risk (Plus a Merkle Proof Check in Python)</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Sun, 25 Oct 2026 08:42:39 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/what-failed-crypto-exchanges-teach-developers-about-custody-risk-plus-a-merkle-proof-check-in-4l0j</link>
      <guid>https://dev.to/nutshellcrypto/what-failed-crypto-exchanges-teach-developers-about-custody-risk-plus-a-merkle-proof-check-in-4l0j</guid>
      <description>&lt;p&gt;Every few years a crypto exchange stops processing withdrawals, and a lot of people learn the same lesson the hard way: a balance on an exchange is a number in someone else's database, not coins you control.&lt;/p&gt;

&lt;p&gt;Mt. Gox, QuadrigaCX, Cryptopia, Thodex, FTX and Bittrex's US business all failed for different reasons. Read their histories side by side, though, and the failure modes start to look like bugs in a system design. That's the angle of this post: what went wrong, why it matters if you build or use crypto products, and one small verification you can actually run.&lt;/p&gt;

&lt;p&gt;For the full list of collapses, dates and recovery figures, see this &lt;a href="https://nutshellcrypto.com/failed-crypto-exchanges/" rel="noopener noreferrer"&gt;list of failed crypto exchanges and what customers got back&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failures were mostly not about price
&lt;/h2&gt;

&lt;p&gt;It's tempting to think exchanges die in crashes. Most of the big ones didn't. They failed because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Theft.&lt;/strong&gt; Mt. Gox lost around 850,000 BTC (about 200,000 were later found). Cryptopia lost roughly a tenth of its assets in a 2019 hack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insider misuse of funds.&lt;/strong&gt; Canada's securities regulator found QuadrigaCX's collapse came from its founder's fraud, with fake accounts and made-up balances. At FTX, an $8 billion hole appeared once withdrawals spiked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulators or law enforcement stepping in.&lt;/strong&gt; BTC-e was seized by US authorities. Bittrex shut its US business weeks after an SEC complaint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In engineering terms: the single source of truth (the exchange's internal ledger) diverged from reality, and nobody outside could see it until withdrawals stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Withdrawals are the health check
&lt;/h2&gt;

&lt;p&gt;In almost every case, the first public symptom was paused or slow withdrawals, usually explained as "technical issues" or "market conditions." If you run anything that holds funds on an exchange, such as a trading bot, a treasury, or a payment flow, it's worth treating withdrawal latency as a metric. Alert on it the way you'd alert on API error rates.&lt;/p&gt;

&lt;p&gt;Recovery is also slow. Mt. Gox creditors waited about a decade, Cryptopia users nearly six years, and FTX customers more than two. FTX paid claims in US dollars at November 2022 prices, so people got dollars back, not the coins they'd held. In the US, FDIC deposit insurance doesn't cover crypto at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of reserves: what you can verify yourself
&lt;/h2&gt;

&lt;p&gt;After FTX, many exchanges started publishing "proof of reserves," often with a Merkle tree of customer balances. The idea: the exchange hashes every account balance into a tree and publishes the root. You get a proof showing your leaf is part of that tree.&lt;/p&gt;

&lt;p&gt;Here's a minimal, self-contained version of that inclusion check:&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;hashlib&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&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;bytes&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;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&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;leaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_hash&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;balance_sats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&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;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;h&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account_hash&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;balance_sats&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;encode&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;build_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;levels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;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;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;  &lt;span class="c1"&gt;# duplicate last node on odd levels
&lt;/span&gt;        &lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;proof_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
        &lt;span class="n"&gt;sib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;proof&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="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sib&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;left&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sib&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_leaf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published_root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_leaf&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sibling&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sibling&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;node&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;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;left&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sibling&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;node&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;published_root&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;accounts&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;a1f3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50_000&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;b7c2&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_200_000&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;c9d0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;75_000&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;me42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;310_000&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;e5aa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9_000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;levels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_tree&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;leaf&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="n"&gt;b&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="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="n"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;proof_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;levels&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my balance included:&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;310_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tampered balance included:&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it prints &lt;code&gt;True&lt;/code&gt; for the real balance and &lt;code&gt;False&lt;/code&gt; for a tampered one. Real exchanges use their own leaf formats (often with salts and per-asset balances), so read their docs before adapting this.&lt;/p&gt;

&lt;p&gt;Know the limits, too. An inclusion proof shows your balance was counted. It doesn't prove the exchange has no hidden debts, that the reserve wallets weren't borrowed for the snapshot, or that things are still fine a week later. It's a useful signal, not a guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lowering your own exposure
&lt;/h2&gt;

&lt;p&gt;The original guide's advice translates well to how developers think about risk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimize blast radius.&lt;/strong&gt; Keep only what you're actively trading on an exchange. Long-term holdings belong in &lt;a href="https://nutshellcrypto.com/self-custody/" rel="noopener noreferrer"&gt;self-custody&lt;/a&gt;, and moving funds to a &lt;a href="https://nutshellcrypto.com/transfer-crypto-to-cold-wallet/" rel="noopener noreferrer"&gt;cold wallet&lt;/a&gt; is a one-time setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer regulated venues.&lt;/strong&gt; Licensing adds custody and disclosure rules. It doesn't remove risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Question yield.&lt;/strong&gt; Celsius and Voyager paid interest by lending out customer crypto. If a return appears, ask where it comes from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep your own records.&lt;/strong&gt; Export statements and trade history regularly. If a platform fails, you'll need them for a claim and for taxes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Exchange balances are IOUs; the ledger can diverge from reality.&lt;/li&gt;
&lt;li&gt;Slow withdrawals have preceded nearly every major collapse, so monitor them.&lt;/li&gt;
&lt;li&gt;Merkle proofs of reserves are verifiable, but they only prove inclusion.&lt;/li&gt;
&lt;li&gt;Recovery usually takes years and may be paid in dollars, not coins.&lt;/li&gt;
&lt;li&gt;Self-custody and good record-keeping shrink your exposure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/failed-crypto-exchanges/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is educational content, not financial or legal advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>security</category>
      <category>python</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Fake Volume Is a Data Quality Bug: Spotting Crypto Wash Trading With a Few API Calls</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:39:24 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/fake-volume-is-a-data-quality-bug-spotting-crypto-wash-trading-with-a-few-api-calls-12n8</link>
      <guid>https://dev.to/nutshellcrypto/fake-volume-is-a-data-quality-bug-spotting-crypto-wash-trading-with-a-few-api-calls-12n8</guid>
      <description>&lt;p&gt;If you've ever built a dashboard on top of crypto market data, you've probably sorted something by 24-hour volume. It's the default "is anyone actually using this?" signal. The uncomfortable part is that volume is one of the easiest numbers in crypto to fake.&lt;/p&gt;

&lt;p&gt;The technique is called &lt;strong&gt;wash trading&lt;/strong&gt;: one person or group trades with themselves, again and again, so a market looks busy. No ownership really changes hands. If you treat volume as ground truth in your app, scanner, or trading bot, you are ingesting manipulated data.&lt;/p&gt;

&lt;p&gt;This post looks at wash trading the way a developer would: as a data quality problem you can partly detect.&lt;/p&gt;

&lt;h2&gt;
  
  
  What wash trading actually is
&lt;/h2&gt;

&lt;p&gt;The CFTC describes wash trades as trades made to give the appearance of buying and selling without taking on market risk or changing the trader's position. You're on both sides, so nothing really happens except fees and a bigger volume number.&lt;/p&gt;

&lt;p&gt;A toy version: a team controls Wallet A and Wallet B. A sells 100,000 tokens to B, then B sells them back a few seconds later. A bot loops that a couple of thousand times a day and the token suddenly "trades" tens of millions of dollars. The team still holds the same coins.&lt;/p&gt;

&lt;p&gt;Why bother? Higher volume moves a token up trending and top-volume lists, can help with exchange listings, sets up pump-and-dumps, inflates exchange rankings, and farms volume-based rewards or airdrop points.&lt;/p&gt;

&lt;p&gt;For the full breakdown of motives, real enforcement cases, and the legal side, see this &lt;a href="https://nutshellcrypto.com/crypto-wash-trading/" rel="noopener noreferrer"&gt;plain-English guide to crypto wash trading and fake volume&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CEX vs DEX: different plumbing, same trick
&lt;/h2&gt;

&lt;p&gt;On a &lt;strong&gt;centralized exchange&lt;/strong&gt;, the manipulator uses multiple accounts or a market maker's bot to place matching buy and sell orders. You only see the exchange's reported numbers, so you're trusting their data feed.&lt;/p&gt;

&lt;p&gt;On a &lt;strong&gt;DEX&lt;/strong&gt;, every trade is on-chain and costs gas, which makes faking volume more expensive but not impossible. A common pattern is one controller wallet funding many fresh wallets, which then trade in the same pool so activity looks spread out.&lt;/p&gt;

&lt;p&gt;One caution for anyone writing detection code: fast back-and-forth trading is not automatically wash trading. Arbitrage and MEV bots also hammer the same pairs within seconds, for legitimate (if competitive) reasons. That's why external heuristics produce red flags, not verdicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick red-flag check in Python
&lt;/h2&gt;

&lt;p&gt;Here's a small script using CoinGecko's public API. It doesn't prove anything; it flags tokens where the numbers don't hang together.&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;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&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;accept&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;application/json&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="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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;r&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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;red_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coin_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&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;https://api.coingecko.com/api/v3/coins/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;coin_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?localization=false&amp;amp;tickers=true&amp;amp;community_data=false&amp;amp;developer_data=false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;md&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_volume&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;mcap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;md&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market_cap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Volume far larger than the whole market cap
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mcap&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;mcap&lt;/span&gt; &lt;span class="o"&gt;&amp;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;flags&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;24h volume is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;mcap&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="s"&gt;x market cap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Volume concentrated on one venue
&lt;/span&gt;    &lt;span class="n"&gt;tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tickers&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;by_venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market&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;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;by_venue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;by_venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;converted_volume&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&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="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by_venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&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;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;top_venue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_vol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by_venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kv&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;top_vol&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;flags&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;top_vol&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; of ticker volume on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;top_venue&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;coin&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bitcoin&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;ethereum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;red_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no obvious red flags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap in any CoinGecko coin ID. Note that the tickers list is paginated and the free API is rate-limited, so treat this as a sketch, not a production monitor.&lt;/p&gt;

&lt;p&gt;The two checks mirror classic warning signs, and a third is worth doing by eye:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Volume vs market cap.&lt;/strong&gt; A small token trading several times its entire value daily, with few holders, deserves suspicion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Venue concentration.&lt;/strong&gt; If nearly all activity sits on one small exchange and big regulated venues show little, the "demand" may not be real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair trust scores.&lt;/strong&gt; CoinGecko's Trust Score on its website looks at things like spread, order book depth and trade patterns. In my testing the free API returned no trust score on tickers, so check it on the coin's markets page. Lots of volume on red or unrated pairs is worth noticing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other checks worth adding if you go further: compare volume to ±2% order book depth, look for volume spikes with no price movement, and on DEXs, cluster wallets by common funding source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't confuse it with the wash sale rule
&lt;/h2&gt;

&lt;p&gt;Different thing entirely. A &lt;strong&gt;wash sale&lt;/strong&gt; is a tax rule about claiming a loss and rebuying within 30 days. If you're scripting tax tooling, that's a separate rabbit hole.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Treat reported crypto volume as untrusted input, especially for small tokens on small venues.&lt;/li&gt;
&lt;li&gt;Combine several heuristics; no single one proves wash trading.&lt;/li&gt;
&lt;li&gt;Don't flag arbitrage and MEV activity as manipulation by default.&lt;/li&gt;
&lt;li&gt;Regulated, larger venues tend to show much cleaner data.&lt;/li&gt;
&lt;li&gt;If your product ranks or alerts on volume, show users the caveats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/crypto-wash-trading/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is educational content, not financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Stablecoin Depegs for Developers: Why 1 USDT Isn't Always $1 (Plus a Python Peg Monitor)</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Fri, 23 Oct 2026 08:41:17 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/stablecoin-depegs-for-developers-why-1-usdt-isnt-always-1-plus-a-python-peg-monitor-3jih</link>
      <guid>https://dev.to/nutshellcrypto/stablecoin-depegs-for-developers-why-1-usdt-isnt-always-1-plus-a-python-peg-monitor-3jih</guid>
      <description>&lt;p&gt;If you've ever built anything that touches crypto payments, a treasury wallet, or a DeFi integration, you've probably treated stablecoins as "basically dollars." That's a reasonable default most of the time. But a stablecoin is a promise to be worth $1, and promises can break. When they do, it's called a depeg, and it's worth understanding before your code (or your savings) assumes 1 USDT always equals $1.&lt;/p&gt;

&lt;p&gt;This post looks at depegs the way an engineer would: what holds the peg up, the failure modes, and a small script to watch prices yourself. For the full plain-English breakdown, with a history table and a safety checklist, NutshellCrypto has a guide on &lt;a href="https://nutshellcrypto.com/are-stablecoins-safe/" rel="noopener noreferrer"&gt;whether stablecoins are safe and why they depeg&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The peg is a feedback loop, not a constant
&lt;/h2&gt;

&lt;p&gt;Fiat-backed coins like USDT and USDC live in two markets. In the primary market, approved firms mint new tokens by sending dollars to the issuer, or redeem tokens for dollars. In the secondary market, everyone else trades existing tokens on exchanges and DeFi apps.&lt;/p&gt;

&lt;p&gt;Arbitrage connects the two. If the token trades at $0.99, someone buys it cheaply and redeems it for $1. If it trades at $1.01, someone mints at $1 and sells. That pressure pulls the price back toward $1.&lt;/p&gt;

&lt;p&gt;The catch is that this loop only works while people trust the issuer can actually pay. If that trust cracks, holders race to redeem first, which looks a lot like a bank run. So the peg is less like a hard-coded constant and more like a control system that can saturate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four ways the loop breaks
&lt;/h2&gt;

&lt;p&gt;Most depegs trace back to one of these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Doubts about the reserves.&lt;/strong&gt; Unclear reports or risky assets make people sell first and ask questions later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserves that are stuck.&lt;/strong&gt; Good assets don't help if they're frozen at a failed bank. USDC dipped to around $0.88 in March 2023 when part of its cash sat at Silicon Valley Bank, then recovered once depositors were protected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto collateral that crashes.&lt;/strong&gt; Crypto-backed coins are over-collateralized, but fast price drops trigger forced sales, the same mechanism behind &lt;a href="https://nutshellcrypto.com/liquidation-crypto/" rel="noopener noreferrer"&gt;liquidations in leveraged trading&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design with no real backing.&lt;/strong&gt; Algorithmic coins hold the peg with code and a companion token. When confidence drops, the two drag each other down. TerraUSD in 2022 is the textbook case.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Venue depeg vs backing depeg
&lt;/h2&gt;

&lt;p&gt;This distinction matters for anyone building on top of price feeds. A &lt;strong&gt;venue depeg&lt;/strong&gt; happens when one exchange runs out of buyers: the coin dips there but stays near $1 everywhere else, and redemptions keep working. A &lt;strong&gt;backing depeg&lt;/strong&gt; happens everywhere at once, because the market believes the reserves are worth less than $1.&lt;/p&gt;

&lt;p&gt;If your app reads a price from a single exchange, a venue depeg can look like a disaster that isn't one. If you average across many venues, you'll smooth out real signals too. Knowing which kind you're looking at is half the battle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch the peg yourself with a few lines of Python
&lt;/h2&gt;

&lt;p&gt;You don't need a paid data feed to keep an eye on the big coins. DefiLlama's public stablecoins endpoint returns prices and circulating supply. Here's a minimal checker:&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;requests&lt;/span&gt;

&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://stablecoins.llama.fi/stablecoins?includePrices=true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;WATCH&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;USDT&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;USDC&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;DAI&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;USDE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;WARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ALERT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;  &lt;span class="c1"&gt;# 0.5% and 2% away from $1
&lt;/span&gt;
&lt;span class="n"&gt;coins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;peggedAssets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;WATCH&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pegType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;peggedUSD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALERT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;ALERT&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;WARN&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;circulating&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;peggedUSD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1e9&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  gap &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="si"&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="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  supply $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;supply&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="s"&gt;B  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran it on October 23, 2026, all four coins were within about 0.1% of $1. A few ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run it on a schedule and send a message only when the status changes.&lt;/li&gt;
&lt;li&gt;Track supply over time. A fast drop in circulating supply means heavy redemptions, which often comes before or alongside a backing depeg.&lt;/li&gt;
&lt;li&gt;Compare against a second source (an exchange ticker, an on-chain DEX pool) to tell a venue depeg from a broad one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat any single feed as one opinion, not ground truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical habits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don't hard-code &lt;code&gt;1 stablecoin == $1&lt;/code&gt; in accounting or risk logic. Read a price and handle the edge case.&lt;/li&gt;
&lt;li&gt;Spread large balances across more than one issuer if they matter to you.&lt;/li&gt;
&lt;li&gt;Know how you'd exit: which venue, which pair, and how fast.&lt;/li&gt;
&lt;li&gt;Remember stablecoins aren't bank deposits and aren't covered by deposit insurance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're newer to the basics, start with this explainer on &lt;a href="https://nutshellcrypto.com/what-are-stablecoins/" rel="noopener noreferrer"&gt;what stablecoins are and how the main types work&lt;/a&gt;, then compare the two biggest coins in &lt;a href="https://nutshellcrypto.com/usdt-vs-usdc/" rel="noopener noreferrer"&gt;USDT vs USDC&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A stablecoin's peg is held by arbitrage and trust, and both can fail.&lt;/li&gt;
&lt;li&gt;Depegs come from reserve doubts, stuck reserves, collateral crashes, or flawed design.&lt;/li&gt;
&lt;li&gt;A venue depeg is local; a backing depeg is everywhere. Your data source decides which one you see.&lt;/li&gt;
&lt;li&gt;A small script on a public API is enough to monitor the big coins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/are-stablecoins-safe/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is educational content, not financial advice. Do your own research before holding any stablecoin.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>blockchain</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Before You Ship a Crypto Trading Bot: API Key Scopes, Kill Switches, and Honest Backtests</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:39:09 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/before-you-ship-a-crypto-trading-bot-api-key-scopes-kill-switches-and-honest-backtests-22m2</link>
      <guid>https://dev.to/nutshellcrypto/before-you-ship-a-crypto-trading-bot-api-key-scopes-kill-switches-and-honest-backtests-22m2</guid>
      <description>&lt;p&gt;Every developer who gets into crypto eventually has the same thought: "I could write a bot for this." And you can. Most big exchanges expose an API, and the core loop of a trading bot fits on one screen. The hard part isn't the loop. It's everything around it: the key you hand the bot, what happens when it misbehaves at 3 a.m., and whether the backtest that convinced you was ever honest.&lt;/p&gt;

&lt;p&gt;This post looks at automated trading the way you'd look at any production service that can spend money. For the plain-English background on bot types and their risks, NutshellCrypto has a full explainer on &lt;a href="https://nutshellcrypto.com/automated-crypto-trading/" rel="noopener noreferrer"&gt;how automated crypto trading and trading bots work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bot is just a loop with a wallet attached
&lt;/h2&gt;

&lt;p&gt;Strip away the marketing and nearly every bot does the same four things: read data (prices, the order book, or just the clock), compare it against rules, place or cancel orders, then repeat. It has no idea where the price is going. A recurring buy, a grid bot and a moving-average crossover bot are all variations on that loop with different rules.&lt;/p&gt;

&lt;p&gt;That framing matters because it tells you where the risk lives. The rules can be wrong, the code can be buggy, and the credentials can leak. None of those risks shrink because the bot calls itself "AI".&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat the API key like a production secret
&lt;/h2&gt;

&lt;p&gt;Your bot talks to the exchange with an API key and secret. Whoever holds that pair can do whatever its permissions allow, so scope it the way you'd scope a cloud IAM role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trade-only.&lt;/strong&gt; The bot needs to place and cancel orders. It almost never needs withdrawal permission, so leave that off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP allowlist.&lt;/strong&gt; If the exchange supports it, lock the key to the server your bot runs on. A leaked key that only works from one IP is far less useful to a thief.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One key per bot.&lt;/strong&gt; Separate keys make it obvious what to revoke when something goes wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No secrets in the repo.&lt;/strong&gt; Load them from environment variables or a secrets manager, rotate them, and delete keys you no longer use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a trade-only key isn't harmless. Someone with it can still place terrible trades on your account, so 2FA on the exchange account and a quick revocation path still matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put guardrails outside the strategy
&lt;/h2&gt;

&lt;p&gt;The strategy decides &lt;em&gt;what&lt;/em&gt; to do. Guardrails decide what the bot is &lt;em&gt;never allowed&lt;/em&gt; to do, no matter what the strategy says. Keep them in a separate layer so a bug in one doesn't disable the other. Here's a minimal sketch with a dry-run mode (no exchange calls, illustrative limits):&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;MAX_ORDER_USD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;25.0&lt;/span&gt;       &lt;span class="c1"&gt;# hard cap per order
&lt;/span&gt;&lt;span class="n"&gt;MAX_DAILY_USD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;      &lt;span class="c1"&gt;# hard cap per day
&lt;/span&gt;&lt;span class="n"&gt;DRY_RUN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DRY_RUN&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;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GuardrailError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Guardrails&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spent_today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usd_amount&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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KILL_SWITCH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;GuardrailError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kill switch file present, refusing to trade&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&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;sell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;GuardrailError&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;unknown side &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;usd_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;usd_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_ORDER_USD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;GuardrailError&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;order size &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;usd_amount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; outside limits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spent_today&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;usd_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_DAILY_USD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;GuardrailError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;daily limit reached&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;place&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usd_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usd_amount&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;DRY_RUN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[dry run] would &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;usd_amount&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&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="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;send_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usd_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spent_today&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;usd_amount&lt;/span&gt;

&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Guardrails&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# prints a dry-run line
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few habits worth copying: default to dry run, make the kill switch something you can trigger without a deploy (a file, a flag, a revoked key), and log every intended order before it's sent so you can reconstruct what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backtests lie politely
&lt;/h2&gt;

&lt;p&gt;A backtest replays your rules over historical data. It's useful, and it's also the easiest place to fool yourself. If you tweak parameters until the equity curve looks great, you've likely fitted noise rather than found an edge. That's overfitting, and it tends to fall apart on new data.&lt;/p&gt;

&lt;p&gt;To keep it honest, test across different market conditions, include every cost (maker/taker fees, spread, slippage), hold out a period you never tuned on, and then paper trade before going live. A bot that trades often can hand most of its gross gains back in fees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The simplest bot is often the sensible one
&lt;/h2&gt;

&lt;p&gt;The lowest-risk automation is a plain recurring buy: a fixed amount on a fixed schedule, no prediction involved. If you want to see what that looks like with your own numbers before writing any code, NutshellCrypto has a free &lt;a href="https://nutshellcrypto.com/tools/dca-calculator/" rel="noopener noreferrer"&gt;crypto DCA calculator&lt;/a&gt; and a guide to &lt;a href="https://nutshellcrypto.com/dollar-cost-averaging-crypto/" rel="noopener noreferrer"&gt;dollar cost averaging in crypto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Leverage is the opposite end. A bot trading futures or margin can get a position force-closed after a short run of small mistakes, which is why spot-only is the safer place to learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spotting the scams
&lt;/h2&gt;

&lt;p&gt;Regulators such as the CFTC, SEC and FTC repeatedly warn that "trading bot" is a favorite cover story for fraud. Red flags: guaranteed or fixed returns, being asked to deposit funds into the bot company's own wallet, surprise "fees" before withdrawals, and "AI" claims with no explanation of the actual strategy. Real automation runs against &lt;em&gt;your&lt;/em&gt; exchange account with a key &lt;em&gt;you&lt;/em&gt; control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A bot follows rules; it can't predict prices.&lt;/li&gt;
&lt;li&gt;Scope API keys to trade-only, add an IP allowlist, and keep secrets out of your repo.&lt;/li&gt;
&lt;li&gt;Keep guardrails (order caps, daily caps, kill switch) separate from strategy code.&lt;/li&gt;
&lt;li&gt;Count fees and slippage, hold out data, and paper trade before risking money.&lt;/li&gt;
&lt;li&gt;Guaranteed returns are a scam signal, not a feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/automated-crypto-trading/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is educational content, not financial, investment or tax advice. Automated strategies can lose money.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>OTC Crypto Desks for Developers: Order-Book Slippage, RFQ Quotes, and Who Sends First</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Wed, 21 Oct 2026 08:38:45 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/otc-crypto-desks-for-developers-order-book-slippage-rfq-quotes-and-who-sends-first-26fn</link>
      <guid>https://dev.to/nutshellcrypto/otc-crypto-desks-for-developers-order-book-slippage-rfq-quotes-and-who-sends-first-26fn</guid>
      <description>&lt;p&gt;If you've only ever bought crypto through an exchange UI, every trade looks like the same thing: pick an amount, hit buy, done. Large trades often don't work that way. Funds, companies and big holders frequently go "over the counter" (OTC), asking a trading desk for one private price instead of hitting the public order book.&lt;/p&gt;

&lt;p&gt;As a developer, the interesting part is that OTC exists because of a mechanical problem you can model in a few lines of code. This post walks through that problem, how a request-for-quote (RFQ) flow behaves, and the risks that live outside the code. For the complete explainer, see NutshellCrypto's guide on &lt;a href="https://nutshellcrypto.com/otc-crypto-trading/" rel="noopener noreferrer"&gt;how OTC crypto trading desks work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: a big market order walks the book
&lt;/h2&gt;

&lt;p&gt;An exchange order book only holds so much size at each price. A large market buy eats the cheapest asks first, then keeps paying more for whatever is left. That gap between the price you expected and the price you actually got is slippage.&lt;/p&gt;

&lt;p&gt;Here's a toy model with made-up numbers (not real market data):&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="c1"&gt;# Illustrative numbers only, not real market data.
&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;101.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;103.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;# (price, size) best first
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;walk_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;asks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;take&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;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&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;cost&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not enough liquidity on the book&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;book_cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;walk_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asks&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;book_cost&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order book: total $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book_cost&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, avg $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&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;slippage vs best ask &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;otc_quote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;101.0&lt;/span&gt;  &lt;span class="c1"&gt;# one all-in price for the full size
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OTC quote:  total $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;otc_quote&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&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;saves $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book_cost&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;otc_quote&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order book: total $3,040.00, avg $101.33, slippage vs best ask 1.33%
OTC quote:  total $3,030.00, saves $10.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a real book the levels thin out much faster for large sizes, and everyone watching sees the price jump while you buy. An OTC desk instead quotes one firm price for the whole block and takes on the job of sourcing the coins across venues and over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  RFQ: a quote with a very short TTL
&lt;/h2&gt;

&lt;p&gt;The OTC flow is basically an RFQ protocol:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Onboard.&lt;/strong&gt; KYC and anti-money-laundering checks first. Legit desks don't skip them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request.&lt;/strong&gt; "Buy 20 BTC with USD", by chat or through an RFQ screen or API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quote.&lt;/strong&gt; The desk returns one all-in price for the full size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accept or expire.&lt;/strong&gt; Quotes are short-lived. Coinbase Prime, for example, documents holding an RFQ price for 3 seconds in its interface and 1 second via API, and its RFQ orders are fill-or-kill: all or nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settle.&lt;/strong&gt; Coins and cash (or stablecoins) move between the two sides.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building anything on top of an RFQ API, treat the quote like a lease with an expiry timestamp: check it locally before you accept, never retry an accept blindly, and handle the "expired" and "rejected" paths as normal outcomes rather than exceptions.&lt;/p&gt;

&lt;p&gt;Minimums are real too. Kraken's OTC desk, for instance, lists a $50,000 USD-equivalent minimum, so this isn't a route most retail buyers need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principal vs agency, in code terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Principal desk:&lt;/strong&gt; the desk is your counterparty. It sells you its own inventory at a firm price and keeps the market risk. Its margin is inside the spread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agency desk:&lt;/strong&gt; the desk is a router. It works your order across markets for a commission, so the price can move while it's filling, and more of that risk stays with you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When comparing, compute the all-in cost: quoted price times amount, plus any commission, wire, network or withdrawal fees. NutshellCrypto has a separate breakdown of &lt;a href="https://nutshellcrypto.com/crypto-exchange-fees/" rel="noopener noreferrer"&gt;crypto exchange fees and spreads&lt;/a&gt; if you want to model the exchange side properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The risks your code can't fix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Counterparty risk.&lt;/strong&gt; You depend on one company to deliver. Genesis, once a major institutional trading provider, froze lending redemptions in November 2022 and later stopped offering trading services entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settlement risk.&lt;/strong&gt; Whoever sends first is exposed until the other side delivers. Know exactly who sends what, when, and to which verified account or wallet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fake "OTC brokers".&lt;/strong&gt; Telegram and social media are full of them, offering below-market coins or "private allocations" and then demanding extra fees before you can withdraw. Reach desks only through a firm's official website, verify payment details through a second channel, and never share a recovery phrase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Taxes.&lt;/strong&gt; An OTC trade is taxed like any other crypto trade, so keep records of each one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OTC exists because large market orders walk the book and slip.&lt;/li&gt;
&lt;li&gt;An RFQ quote is a short-lived, often fill-or-kill price; design for expiry.&lt;/li&gt;
&lt;li&gt;Principal desks take the risk and earn the spread; agency desks route and charge a fee.&lt;/li&gt;
&lt;li&gt;Counterparty and settlement risk matter as much as the price.&lt;/li&gt;
&lt;li&gt;For small buys, a normal exchange order book is usually enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/otc-crypto-trading/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is educational content, not financial, investment or tax advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>fintech</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Polkadot Staking in 2026, Straight From the Chain: Minimums, 2-Day Unbonding, and a Python Check</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:45:34 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/polkadot-staking-in-2026-straight-from-the-chain-minimums-2-day-unbonding-and-a-python-check-1dk6</link>
      <guid>https://dev.to/nutshellcrypto/polkadot-staking-in-2026-straight-from-the-chain-minimums-2-day-unbonding-and-a-python-check-1dk6</guid>
      <description>&lt;p&gt;If you search for how DOT staking works, a lot of what comes back is stale. Older tutorials still talk about a 28-day lock-up, slashing risk for nominators, and double-digit yields. Polkadot changed most of that through on-chain governance in 2025 and 2026, so the safest habit is the one developers already have: read the live state instead of trusting a blog post, including this one.&lt;/p&gt;

&lt;p&gt;This post covers what changed, what the numbers mean, and a short Python script that pulls the key staking values from Polkadot Hub yourself. For the full walkthrough (wallets, pools vs direct nominating, unstaking steps, US and Canada taxes), see NutshellCrypto's &lt;a href="https://nutshellcrypto.com/polkadot-staking/" rel="noopener noreferrer"&gt;Polkadot staking guide on how to stake DOT, rewards and risks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The model in one paragraph
&lt;/h2&gt;

&lt;p&gt;Polkadot uses Nominated Proof-of-Stake. Validators run the nodes. Nominators bond DOT and pick up to 16 validators to back, and the protocol decides which of those picks your stake actually supports each era (about 24 hours). Your DOT never leaves your account; bonding locks it, it doesn't send it anywhere. You can nominate directly from 250 DOT, or join a nomination pool from 1 DOT and let the pool operator choose validators.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's different now
&lt;/h2&gt;

&lt;p&gt;A few changes matter if your mental model is from 2024:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Staking moved to Polkadot Hub.&lt;/strong&gt; In November 2025, balances, staking and governance migrated from the Relay Chain to Asset Hub (now Polkadot Hub). Wallets and the Staking Dashboard read your position from there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nominators can't be slashed.&lt;/strong&gt; Since July 6, 2026, nominator slashing is switched off by governance. Validators still carry slashing risk and must self-bond at least 10,000 DOT.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unbonding is about 2 days.&lt;/strong&gt; Nominator unbonding dropped from 28 days to 2 eras. Requests started before the change stay on the old schedule unless you rebond and unbond again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commission is 0% and issuance is lower.&lt;/strong&gt; Validator commission was set to 0%, and yearly issuance was cut sharply in March 2026, so yields are now low single digits. The NutshellCrypto guide worked it out to roughly 2.8% a year before any pool fee on October 20, 2026.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are governance settings, not permanent laws of physics. That's exactly why checking the chain is worth a few lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the live values with Python
&lt;/h2&gt;

&lt;p&gt;Polkadot exposes storage through public RPC nodes, and the &lt;code&gt;substrate-interface&lt;/code&gt; package decodes it using the chain's own metadata. Install it in a virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;substrate-interface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then query the values a new staker actually cares about:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;PLANCK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# 1 DOT = 10^10 planck
&lt;/span&gt;&lt;span class="n"&gt;hub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SubstrateInterface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wss://polkadot-asset-hub-rpc.polkadot.io&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;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;PLANCK&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&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;hub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Current era:       &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Staking&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;CurrentEra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Min nominator bond:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Staking&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;MinNominatorBond&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;DOT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Min active stake:  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Staking&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;MinimumActiveStake&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;DOT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Min pool join bond:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NominationPools&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;MinJoinBond&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;DOT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Nominators:        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Staking&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;CounterForNominators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validator count:   &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Staking&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;ValidatorCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran it on October 20, 2026, it printed era 2329, a 250 DOT nominator minimum, a minimum active stake of about 191 DOT, a 1 DOT pool minimum, about 28,300 nominators, and 600 validators. Your output will differ, and that's the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read the output
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Min nominator bond vs min active stake.&lt;/strong&gt; These are easy to confuse. The first is what you need to start nominating. The second is set by the smallest nominator who made it into the last election, and it moves every era. The election only considers about 22,500 nominators, and there are more registered than that, so a nomination below the active threshold can sit there earning nothing. If you're close to the line, a pool is usually the simpler choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pool minimum.&lt;/strong&gt; One DOT gets you into a pool, but pools charge their own commission (0% to 10%) and the pool itself has to stay active to earn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Units.&lt;/strong&gt; Everything on-chain is in planck. Forgetting the 10^10 divisor is the classic off-by-a-lot bug when you build dashboards or alerts on top of this data.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few security notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Staking Dashboard is a staking app, not a wallet. Your recovery phrase belongs offline and never goes into a website, a "support" DM, or a form.&lt;/li&gt;
&lt;li&gt;Direct nominators' rewards must be claimed within 84 eras or they're lost. Validators usually trigger payouts, but if you're building a monitor, this is a good thing to alert on.&lt;/li&gt;
&lt;li&gt;Liquid staking tokens skip the unbonding wait but add smart-contract and depeg risk. Know which risk you're taking on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a contrast, NutshellCrypto's &lt;a href="https://nutshellcrypto.com/cosmos-staking/" rel="noopener noreferrer"&gt;Cosmos Hub staking guide&lt;/a&gt; covers a chain where delegators can still be slashed and unbonding takes 21 days. If the whole concept is new, start with &lt;a href="https://nutshellcrypto.com/what-is-staking-in-crypto/" rel="noopener noreferrer"&gt;what staking in crypto means&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Nominate from 250 DOT, or join a pool from 1 DOT.&lt;/li&gt;
&lt;li&gt;Nominators are currently unslashable, and unbonding takes about 2 days.&lt;/li&gt;
&lt;li&gt;Yields are low single digits after the 2026 issuance cut.&lt;/li&gt;
&lt;li&gt;The minimum active stake moves every era, so check it before you bond.&lt;/li&gt;
&lt;li&gt;Query the chain yourself; older guides are often out of date.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/polkadot-staking/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is for education only and is not financial, tax, or investment advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Scripting Your Kraken Tax Exports: Proceeds, Missing Basis, and a Python Sanity Check</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:39:34 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/scripting-your-kraken-tax-exports-proceeds-missing-basis-and-a-python-sanity-check-dg</link>
      <guid>https://dev.to/nutshellcrypto/scripting-your-kraken-tax-exports-proceeds-missing-basis-and-a-python-sanity-check-dg</guid>
      <description>&lt;p&gt;Every tax season, someone who writes code for a living opens a folder of exchange exports and thinks, "I'll just script this." That is a good instinct, as long as you know what each file actually means before you start summing columns. Kraken is a useful case study because it hands you several different documents, and they do not all answer the same question.&lt;/p&gt;

&lt;p&gt;This post covers the data side: which Kraken files exist, what the IRS receives versus what only you see, and a small Python sanity check you can run on your exports. For the click-by-click download steps, Canada notes, and FAQ, see NutshellCrypto's &lt;a href="https://nutshellcrypto.com/kraken-tax-report/" rel="noopener noreferrer"&gt;Kraken tax report guide to the Tax Center, 1099-DA, and CSV exports&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two places to look, and both are web-only
&lt;/h2&gt;

&lt;p&gt;Kraken splits tax data across two areas, and per Kraken Support neither is available in the mobile apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tax Center&lt;/strong&gt; (profile icon, then Settings, then Tax Center): IRS forms plus three CSV reports for the selected year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Center&lt;/strong&gt; (profile icon, then Documents): raw exports such as ledger, trades, balances, and account statements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Tax Center CSVs are summaries. The Document Center files are closer to raw events. If your goal is reproducible numbers, keep both, because large exports can take a while to generate and Kraken says finished exports stay downloadable for only about 14 days. Save local copies the day you get them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know what each CSV is for
&lt;/h2&gt;

&lt;p&gt;The Tax Center gives you three reports:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Income Report&lt;/strong&gt;: staking rewards, airdrops, referral bonuses, and similar earnings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gain/Loss Report&lt;/strong&gt;: realized disposals with proceeds, cost basis, and short- or long-term splits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction History Report&lt;/strong&gt;: everything for the year, which is the file you want for reconciliation or importing into tax software.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Treat the Gain/Loss Report as a worksheet, not a final answer. Its math depends on the lot method and on whether Kraken actually knows your cost basis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proceeds are not profit
&lt;/h2&gt;

&lt;p&gt;This is the part that trips up anyone who "just sums the 1099." For tax year 2025, Kraken reports &lt;strong&gt;gross proceeds&lt;/strong&gt; to the IRS on Form 1099-DA. The cost basis and gain figures on your copy are calculated with FIFO for convenience and were not reported to the IRS for that year. Kraken says that starting with tax year 2026 it will also report basis, acquisition date, and gain or loss for positions acquired on or after January 1, 2026.&lt;/p&gt;

&lt;p&gt;So the number on the form is a sales total, not your taxable gain. Your gain is proceeds minus basis, and basis is the field most likely to be wrong or missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Missing basis is not zero basis
&lt;/h2&gt;

&lt;p&gt;If you moved coins into Kraken from another wallet or exchange, Kraken often cannot see what you paid. Those rows show blank or unknown basis. Kraken's own FAQ is explicit that missing basis does not mean $0, and treating it as zero overstates your tax. A move between your own wallets is usually not a sale either, which NutshellCrypto covers in its &lt;a href="https://nutshellcrypto.com/transfer-crypto-taxes/" rel="noopener noreferrer"&gt;guide to whether transferring crypto is taxable&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick sanity check in Python
&lt;/h2&gt;

&lt;p&gt;Here is a small script that flags the two most common data problems before you import anything: disposals with no basis, and trades close enough to midnight on New Year's Eve that a UTC timestamp could push them into a different tax year. Column names differ between export versions, so open your file first and edit the three constants to match its header row.&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;csv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="c1"&gt;# Edit these to match the header row of YOUR export.
&lt;/span&gt;&lt;span class="n"&gt;DATE_COL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PROCEEDS_COL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proceeds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;BASIS_COL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_basis&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;parse_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromisoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Z&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;+00:00&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="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tzinfo&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;dt&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="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&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;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&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="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&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;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&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;as&lt;/span&gt; &lt;span class="n"&gt;f&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;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&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="n"&gt;start&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="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PROCEEDS_COL&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASIS_COL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;missing&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="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DATE_COL&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&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;1&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                &lt;span class="n"&gt;edge&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="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total proceeds: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&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="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rows with blank basis (fill in, don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t assume 0): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="ow"&gt;or&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="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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dec 31 / Jan 1 UTC rows to double-check: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="ow"&gt;or&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="si"&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;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;python check_kraken.py gain_loss_2025.csv&lt;/code&gt;. If the total proceeds roughly line up with the 1099-DA, your export is complete. If they don't, pull ledger history from the Document Center before going further.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the exchange files won't cover
&lt;/h2&gt;

&lt;p&gt;Kraken notes that Kraken Wallet and other DeFi activity are outside the first wave of exchange 1099-DA reporting, and that a 1099-MISC only appears for rewards of $600 or more. Small rewards are still income. If you also use other platforms, keep each one's CSVs separate and label transfers so your software doesn't count a move as a sale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Download from the web, and save Document Center exports before they expire.&lt;/li&gt;
&lt;li&gt;For 2025, the 1099-DA shows gross proceeds, not your gain.&lt;/li&gt;
&lt;li&gt;Blank basis means "unknown," never zero.&lt;/li&gt;
&lt;li&gt;Watch for UTC date shifts around December 31.&lt;/li&gt;
&lt;li&gt;Rewards under $600 and DeFi activity still need your own records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/kraken-tax-report/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is for education only and is not financial, tax, or investment advice. Talk to a qualified tax professional about your situation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>USDT and USDC Are Not the Same Integer: What Developers Should Check Before Accepting Either</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Sat, 17 Oct 2026 08:45:37 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/usdt-and-usdc-are-not-the-same-integer-what-developers-should-check-before-accepting-either-129p</link>
      <guid>https://dev.to/nutshellcrypto/usdt-and-usdc-are-not-the-same-integer-what-developers-should-check-before-accepting-either-129p</guid>
      <description>&lt;p&gt;If you build anything that touches payments, a wallet, or a trading bot, you will eventually see code like &lt;code&gt;if token in ("USDT", "USDC"): value = amount&lt;/code&gt;. Both read $1.00 on a screen, so it is tempting to treat them as the same dollar. They are not. They come from different issuers, hold different reserves, follow different rules, and on-chain they are separate contracts with their own admin powers.&lt;/p&gt;

&lt;p&gt;This post is the developer checklist: what actually differs, and a small Python script that reads both contracts directly. For the full side-by-side (issuers, reserve breakdowns, ratings, chains), see NutshellCrypto's &lt;a href="https://nutshellcrypto.com/usdt-vs-usdc/" rel="noopener noreferrer"&gt;USDT vs USDC comparison of reserves, rules, and risks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two issuers, two balance sheets
&lt;/h2&gt;

&lt;p&gt;USDT is issued by Tether, launched in 2014, and is the bigger coin: about $183.5B market cap versus about $72.6B for USDC, per CoinGecko on October 16, 2026. USDC is issued by Circle, whose parent is listed on the NYSE.&lt;/p&gt;

&lt;p&gt;The backing is where they split:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;USDT&lt;/strong&gt; reported $187.75B in reserves against $183.64B in liabilities on June 30, 2026. Most of it is Treasury bills and repos, but it also holds gold, bitcoin, and secured loans. Checks are quarterly attestations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USDC&lt;/strong&gt; reported $73.40B in reserves for $73.32B in circulation on August 31, 2026, almost all in a BlackRock-managed government money market fund plus bank cash. Checks are monthly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So USDT has a bigger cushion made partly of volatile assets; USDC has a thin cushion made of very liquid ones. If your app holds large balances for users, that difference belongs in your risk notes, not just your UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't hardcode 1.00
&lt;/h2&gt;

&lt;p&gt;Both coins have broken the peg briefly. USDT fell to about $0.92 in October 2018 and around $0.95 during the TerraUSD collapse in May 2022. USDC dropped to about $0.88 in March 2023 when Circle had reserves stuck at Silicon Valley Bank. Each recovered, but a bot or accounting system that assumed exactly $1 would have mispriced positions during those windows.&lt;/p&gt;

&lt;p&gt;Practical rule: price stablecoins from a feed like any other asset, and alert when the price drifts outside a band you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same ticker, different contracts per chain
&lt;/h2&gt;

&lt;p&gt;USDT and USDC both live on many networks, and every version is a separate token contract. USDT is mostly on Tron and Ethereum; USDC is mostly on Ethereum, with Circle listing native USDC on dozens of chains. Sending USDT on Tron to an address that only watches Ethereum is a classic way to lose funds.&lt;/p&gt;

&lt;p&gt;For developers that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key your token registry by &lt;code&gt;(chain, contract_address)&lt;/code&gt;, never by ticker.&lt;/li&gt;
&lt;li&gt;Read &lt;code&gt;decimals()&lt;/code&gt; from each contract instead of assuming. Both use 6 on Ethereum, but bridged or chain-specific versions don't have to match.&lt;/li&gt;
&lt;li&gt;Show users the network clearly before they send. NutshellCrypto's &lt;a href="https://nutshellcrypto.com/crypto-wallet-address/" rel="noopener noreferrer"&gt;guide to wallet addresses and networks&lt;/a&gt; covers the user side of this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Both can freeze your tokens
&lt;/h2&gt;

&lt;p&gt;This surprises people who assume self-custody means nobody can touch their balance. Both issuers can blacklist addresses at the contract level, usually for law enforcement or sanctions requests. Your keys still control the address, but the token contract can refuse to move the balance.&lt;/p&gt;

&lt;p&gt;You can check this yourself. The script below calls the Ethereum mainnet contracts over plain JSON-RPC (standard library only) and reads decimals, total supply on that chain, and whether a given address is blacklisted. Note the two contracts even spell the function differently: USDT uses &lt;code&gt;isBlackListed&lt;/code&gt;, USDC uses &lt;code&gt;isBlacklisted&lt;/code&gt;.&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;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="n"&gt;RPC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://ethereum-rpc.publicnode.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;TOKENS&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;USDT&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;0xdAC17F958D2ee523a2206206994597C13D831ec7&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;0xe47d6060&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# isBlackListed(address)
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USDC&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;0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&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;0xfe575a87&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# isBlacklisted(address)
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;DECIMALS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOTAL_SUPPLY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x313ce567&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;0x18160ddd&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;eth_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jsonrpc&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;2.0&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;id&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method&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;eth_call&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;params&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;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&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;body&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;Content-Type&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;application/json&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;User-Agent&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;stable-check/0.1&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="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&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;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;16&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;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;removeprefix&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;rjust&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="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="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blacklist_sel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TOKENS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eth_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DECIMALS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eth_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOTAL_SUPPLY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;dec&lt;/span&gt;
        &lt;span class="n"&gt;frozen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eth_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blacklist_sel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: decimals=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  supply on Ethereum=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;supply&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  frozen=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frozen&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;check&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap in any address you want to inspect. Public RPCs rate-limit, so use your own node or provider for anything beyond a quick check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules decide what you can list
&lt;/h2&gt;

&lt;p&gt;Regulation now shapes which coin a product can offer. USDC is an authorized e-money token under the EU's MiCA rules, and major exchanges restricted USDT trading for EU users. Canada's registered platforms offer USDC but not USDT. In the US, the GENIUS Act sets up licensing for stablecoin issuers, and Tether's US-regulated option is a separate token, USAT. If you ship to multiple regions, your supported-asset list may need to vary by jurisdiction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Treat USDT and USDC as different assets with different issuers and reserve profiles.&lt;/li&gt;
&lt;li&gt;Never hardcode a $1 price; monitor drift like any other asset.&lt;/li&gt;
&lt;li&gt;Identify tokens by chain and contract address, and read &lt;code&gt;decimals()&lt;/code&gt; per contract.&lt;/li&gt;
&lt;li&gt;Both issuers can freeze addresses at the contract level, so check before you rely on a balance.&lt;/li&gt;
&lt;li&gt;Regional rules (MiCA, Canada, the GENIUS Act) can decide which coin you're allowed to offer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New to the basics? NutshellCrypto's &lt;a href="https://nutshellcrypto.com/what-are-stablecoins/" rel="noopener noreferrer"&gt;explainer on what stablecoins are and how the peg works&lt;/a&gt; is a good starting point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/usdt-vs-usdc/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is for education only and is not financial or investment advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>python</category>
      <category>security</category>
    </item>
    <item>
      <title>Cosmos Hub Staking for Developers: Voting Power, 21-Day Unbonding, and an LCD Check in Python</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Fri, 16 Oct 2026 08:40:04 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/cosmos-hub-staking-for-developers-voting-power-21-day-unbonding-and-an-lcd-check-in-python-5814</link>
      <guid>https://dev.to/nutshellcrypto/cosmos-hub-staking-for-developers-voting-power-21-day-unbonding-and-an-lcd-check-in-python-5814</guid>
      <description>&lt;p&gt;Most proof-of-stake guides talk about "locking coins with a validator." On the Cosmos Hub that framing is half right and half misleading. When you stake ATOM you are not handing custody to an operator. You are bonding voting power to one of the top 180 validators. Your keys stay yours. What you share is revenue after commission, and proportional slashing if that operator double-signs or stays offline too long.&lt;/p&gt;

&lt;p&gt;This post is the developer mental model: what a &lt;code&gt;MsgDelegate&lt;/code&gt; actually changes, why exit takes 21 days, how redelegation skips that wait, and a short Python check against a public LCD before you pick a validator. For the full beginner walkthrough (Keplr, Ledger, taxes, Liquid Staking Module), see NutshellCrypto's &lt;a href="https://nutshellcrypto.com/cosmos-staking/" rel="noopener noreferrer"&gt;Cosmos staking guide on how ATOM delegation, rewards, and risks work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Voting power, not custody
&lt;/h2&gt;

&lt;p&gt;A Cosmos Hub validator cannot withdraw your ATOM. Official docs are clear: delegation moves voting power, not ownership. Your real risks are different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slashing.&lt;/strong&gt; Double-signing is a 5% slash of the validator's total stake (yours included). Prolonged downtime is a 0.01% slash, then jail, if they miss more than 95% of the last 10,000 blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Illiquidity.&lt;/strong&gt; Undelegating starts a 21-day unbonding period with no rewards. Stake can still be slashed for faults that happened before you started unbonding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance inheritance.&lt;/strong&gt; If you do not vote on Hub proposals, you inherit your validator's vote. If you do vote, you override it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is easy to miss if you treat staking like a savings product. On Cosmos, your chosen operator is also your default ballot until you cast your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers that actually matter
&lt;/h2&gt;

&lt;p&gt;Live Hub params (they can change via governance):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Typical Hub value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Active set&lt;/td&gt;
&lt;td&gt;Top 180 validators by total stake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unbonding&lt;/td&gt;
&lt;td&gt;21 days (&lt;code&gt;1814400s&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bond denom&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;uatom&lt;/code&gt; (1 ATOM = 1,000,000 uatom)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Min commission&lt;/td&gt;
&lt;td&gt;5% protocol floor (when last checked)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redelegate&lt;/td&gt;
&lt;td&gt;Immediate, with limits on in-flight entries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rewards come from block provisions and fees after commission. There is no fixed APY. Hub rewards usually sit as claimable until you withdraw them, or until you authorize a restake-style app. Those grants are useful and also a trust boundary: read the permissions and revoke unused ones.&lt;/p&gt;

&lt;p&gt;Redelegation is the Hub's pressure valve. If uptime collapses or commission max looks scary, you can move stake without waiting three weeks. Undelegate only when you want liquid ATOM again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Hub params and a validator in Python
&lt;/h2&gt;

&lt;p&gt;Public LCD endpoints expose the staking module. This script prints unbonding time, max validators, min commission, bonded pool size, and basic fields for validator operator addresses (&lt;code&gt;cosmosvaloper...&lt;/code&gt;):&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;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;LCD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://cosmos-rest.publicnode.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# any reliable Hub LCD
&lt;/span&gt;&lt;span class="n"&gt;UATOM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;staking_params&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LCD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/cosmos/staking/v1beta1/params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unbonding_days&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unbonding_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_validators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_validators&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;min_commission&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_commission_rate&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;bond_denom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bond_denom&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;max_redelegation_entries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_entries&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pool_snapshot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LCD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/cosmos/staking/v1beta1/pool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pool&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bonded_atom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bonded_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;UATOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not_bonded_atom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not_bonded_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;UATOM&lt;/span&gt;&lt;span class="p"&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;check_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valoper&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;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LCD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/cosmos/staking/v1beta1/validators/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;valoper&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="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;rates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;commission&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;commission_rates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jailed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;flags&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jailed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BOND_STATUS_BONDED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;flags&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;status=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_rate&lt;/span&gt;&lt;span class="sh"&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="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;flags&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;max commission can rise to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;max_rate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&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="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;moniker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&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;moniker&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;commission&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&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;max_commission&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_rate&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;tokens_atom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;UATOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bonded and not jailed&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;staking_params&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pool_snapshot&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;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;check_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran this on October 16, 2026, Hub params reported 21-day unbonding, &lt;code&gt;max_validators&lt;/code&gt; 180, a 5% min commission floor, and about 338 million ATOM in the bonded pool. Pass real &lt;code&gt;cosmosvaloper...&lt;/code&gt; addresses from Mintscan or your wallet. Also check self-bond, uptime, and governance participation. Spreading stake across a few solid operators beats parking everything on one large name.&lt;/p&gt;

&lt;p&gt;Caveats: the bonded-pool figure is not the same as percent of circulating supply. A low current commission with a high max can jump later. Double-signing is rare but severe, so reputation and infra matter more than a 1% fee difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exchange staking vs Hub delegation
&lt;/h2&gt;

&lt;p&gt;Exchanges often advertise ATOM staking as a product. That is usually custodial: they hold the coins, pick validators, and set their own lock rules. Self-custody Hub delegation keeps keys with you and matches protocol unbonding. Simpler UX on an exchange is real; so is exchange credit risk. Neither path has a promised yield.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hub staking delegates voting power; validators cannot withdraw your ATOM, but slashing still hits your bonded share.&lt;/li&gt;
&lt;li&gt;Undelegate = 21 days with no rewards. Redelegate = switch validators immediately (within Hub entry limits).&lt;/li&gt;
&lt;li&gt;Claim rewards yourself, or review restake grants carefully.&lt;/li&gt;
&lt;li&gt;If you skip Hub votes, you inherit your validator's vote.&lt;/li&gt;
&lt;li&gt;Query LCD params and validator status before you bond; treat advertised APYs as estimates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New to staking concepts? NutshellCrypto's &lt;a href="https://nutshellcrypto.com/what-is-staking-in-crypto/" rel="noopener noreferrer"&gt;explainer on what staking in crypto means&lt;/a&gt; covers the shared vocabulary across chains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/cosmos-staking/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is for education only and is not financial, investment, or tax advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Cardano Staking for Developers: Delegation Is a Certificate, Not a Transfer (Plus a Pool Check in Python)</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Thu, 15 Oct 2026 08:49:27 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/cardano-staking-for-developers-delegation-is-a-certificate-not-a-transfer-plus-a-pool-check-in-19df</link>
      <guid>https://dev.to/nutshellcrypto/cardano-staking-for-developers-delegation-is-a-certificate-not-a-transfer-plus-a-pool-check-in-19df</guid>
      <description>&lt;p&gt;Most proof-of-stake chains make you lock coins, hand them to a validator, and accept that a misbehaving operator can get part of your stake slashed. Cardano works differently, and if you think in terms of state and permissions it is a neat design: delegating ADA does not move your coins at all. It just attaches a certificate to your stake key that says "count my balance toward this pool."&lt;/p&gt;

&lt;p&gt;This post walks through that model the way a developer would look at it, then shows a short Python script that checks a stake pool's numbers through the public Koios API before you delegate. For the full beginner walkthrough, including exchange options and US and Canada tax notes, see NutshellCrypto's &lt;a href="https://nutshellcrypto.com/ada-staking/" rel="noopener noreferrer"&gt;ADA staking guide on how rewards and risks work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model: permissions, not custody
&lt;/h2&gt;

&lt;p&gt;A Cardano address has two parts that matter here: a payment part (who can spend the coins) and a stake part (who gets credit for that stake). Delegation only touches the stake part. You sign one transaction that registers your stake key and submits a delegation certificate pointing it at a pool.&lt;/p&gt;

&lt;p&gt;That has some useful consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No lock-up.&lt;/strong&gt; Your ADA stays spendable the whole time. Spending some just lowers the stake counted in the next snapshot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No minimum.&lt;/strong&gt; Any balance counts, and the wallet delegates the whole balance tied to that stake key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No slashing.&lt;/strong&gt; A bad pool can cost you rewards, but the protocol never takes delegated ADA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A refundable deposit.&lt;/strong&gt; Registering a stake key for the first time costs a 2 ADA deposit, which you get back when you deregister it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The operator never gets your keys. If anyone, whether a "pool support" account or a wallet pop-up, asks for your recovery phrase to stake, it's a scam.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the first reward takes 15 to 20 days
&lt;/h2&gt;

&lt;p&gt;Cardano runs in 5-day epochs, and pools are chosen to make blocks based on a stake snapshot. So a new delegation moves through a pipeline: you delegate in epoch N, the snapshot picks it up at the next boundary, the stake is active in N+2, rewards are calculated in N+3, and they're paid in N+4. After that first wait, rewards land every epoch as long as the pool makes blocks, and they compound automatically because the reward account counts toward your stake.&lt;/p&gt;

&lt;p&gt;One recent change catches people out. Since the Plomin hard fork, you can only withdraw rewards once your stake key also has a vote delegation (a DRep, Abstain, or No Confidence). Rewards keep piling up without it; you just can't move them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking a pool with Python before you delegate
&lt;/h2&gt;

&lt;p&gt;Pool choice is the one decision you actually control, and the inputs are all on-chain. The things worth checking are the margin (the operator's percentage), the fixed cost per epoch, saturation (beyond it, rewards per ADA drop for everyone in the pool), and whether the owners are actually meeting their declared pledge. If live pledge falls below declared pledge, the pool's rewards for that epoch are zero.&lt;/p&gt;

&lt;p&gt;Koios is a free community API for Cardano chain data. This script pulls the current protocol parameters and flags common red flags for one or more pool IDs:&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;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;KOIOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.koios.rest/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LOVELACE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;  &lt;span class="c1"&gt;# 1 ADA = 1,000,000 lovelace
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;protocol_params&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KOIOS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/cli_protocol_params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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;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;stake_key_deposit_ada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stakeAddressDeposit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;LOVELACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stakePoolTargetNum&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;min_fixed_cost_ada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minPoolCost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;LOVELACE&lt;/span&gt;&lt;span class="p"&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;check_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KOIOS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/pool_info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;json&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;_pool_bech32_ids&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;pool_id&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pool_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;registered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retiring_epoch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;flags&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pool is retiring or not registered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;live_pledge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pledge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;flags&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;live pledge below declared pledge (rewards can be zero)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;live_saturation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;flags&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;near saturation (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;live_saturation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;margin&lt;/span&gt;&lt;span class="sh"&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="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;flags&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;margin is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;margin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&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="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;margin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;margin&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;fixed_cost_ada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fixed_cost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;LOVELACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;live_stake_ada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;live_stake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;LOVELACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saturation_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;live_saturation&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;lifetime_blocks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;block_count&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;flags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no obvious red flags&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;protocol_params&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;pid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;check_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with one or more &lt;code&gt;pool1...&lt;/code&gt; IDs copied from a pool explorer or your wallet. When I ran it on October 15, 2026, Koios reported a 2 ADA stake key deposit, a target of 500 pools (the k parameter), and a 170 ADA minimum fixed cost per epoch. The 5% margin and 90% saturation cutoffs are my own arbitrary picks for the example, so adjust them to taste.&lt;/p&gt;

&lt;p&gt;A couple of caveats. One epoch of low blocks doesn't mean a pool is bad, since small pools get picked less often and their rewards vary more, so look at performance over many epochs. And treat the script as a filter, not a ranking: a pool with low fees that is often offline will still pay you less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exchange staking vs delegating yourself
&lt;/h2&gt;

&lt;p&gt;Exchanges like Coinbase offer ADA staking in some regions. It's simpler, but it's custodial: the exchange holds the coins, chooses the pool, and takes a cut. With native delegation, the worst a pool can do is earn you nothing. With an exchange, you're also exposed to frozen withdrawals or the exchange failing. Either way, there's no fixed APY: rewards depend on pool performance, fees, and total stake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Delegation on Cardano is a certificate on your stake key, not a transfer, so there's no lock-up and no slashing.&lt;/li&gt;
&lt;li&gt;Expect the first reward about 15 to 20 days in, then every 5-day epoch.&lt;/li&gt;
&lt;li&gt;Set a vote delegation, or you won't be able to withdraw rewards.&lt;/li&gt;
&lt;li&gt;Check margin, fixed cost, saturation, and pledge before choosing a pool; the Koios API makes that scriptable.&lt;/li&gt;
&lt;li&gt;In the US and Canada, staking rewards are generally treated as income when you receive them, so log each payout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comparing chains? NutshellCrypto's &lt;a href="https://nutshellcrypto.com/what-is-staking-in-crypto/" rel="noopener noreferrer"&gt;guide to how staking works in crypto&lt;/a&gt; covers the basics behind all of this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nutshellcrypto.com/ada-staking/" rel="noopener noreferrer"&gt;Read the full guide on NutshellCrypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is for education only and is not financial, investment, or tax advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Crypto.com Dropped Its Tax Calculator: Build Your File From CSVs and 1099-DA Instead</title>
      <dc:creator>Huzaifa Zahoor</dc:creator>
      <pubDate>Wed, 14 Oct 2026 08:45:17 +0000</pubDate>
      <link>https://dev.to/nutshellcrypto/cryptocom-dropped-its-tax-calculator-build-your-file-from-csvs-and-1099-da-instead-57hn</link>
      <guid>https://dev.to/nutshellcrypto/cryptocom-dropped-its-tax-calculator-build-your-file-from-csvs-and-1099-da-instead-57hn</guid>
      <description>&lt;p&gt;Crypto.com used to ship a built-in Tax calculator that spat out a yearly capital-gains style report. That product was deprecated in June 2024. For 2026 filing you are back to the boring path every serious exchange workflow already uses: export complete CSVs from the App and Exchange, save any IRS information returns Crypto.com emails you, then reconcile in software or your own ledger.&lt;/p&gt;

&lt;p&gt;This post is a developer-oriented map of what those exports give you, where the 1099-DA trap sits, and a small Python sketch to sanity-check App Token Wallet CSVs. For click-by-click paths and a Canada note, see NutshellCrypto's guide to the &lt;a href="https://nutshellcrypto.com/crypto-com-tax-report/" rel="noopener noreferrer"&gt;Crypto.com tax report&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What replaced "Crypto.com Tax"
&lt;/h2&gt;

&lt;p&gt;Crypto.com's Tax site and Help Center say the legacy calculator is no longer operational for new reports, and that past reports from that tool are no longer applicable. They point users toward partner tax services and toward exporting transaction history yourself.&lt;/p&gt;

&lt;p&gt;Practical takeaway: do not wait for a fresh Crypto.com Tax PDF. Treat App + Exchange CSVs (plus any Forms 1099) as the source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to download (App vs Exchange)
&lt;/h2&gt;

&lt;p&gt;From the &lt;strong&gt;Crypto.com App&lt;/strong&gt;, Help documents three CSV sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token Wallet&lt;/strong&gt; — crypto activity; usually the primary import for tax software&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cash Wallet&lt;/strong&gt; — fiat&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto.com Visa Card&lt;/strong&gt; — card spend history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App exports can cover up to about three years per report, and downloads typically expire after 30 days. For a US calendar year, export at least January 1–December 31 of the tax year, plus earlier years if you still hold coins bought then (cost basis often lives in older buys).&lt;/p&gt;

&lt;p&gt;If you also use &lt;strong&gt;Crypto.com Exchange&lt;/strong&gt;, App CSVs alone are incomplete. Exchange exports cover wallet transactions, deposits/withdrawals, order and trade history, and related sets — with date-range caps and a ~65,000-row limit on some files. Shorten the range and stitch months if an export truncates.&lt;/p&gt;

&lt;p&gt;Keep App and Exchange files separate and mark internal transfers so software does not invent a taxable sale when you moved coins between Crypto.com products.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 1099-DA gap: proceeds ≠ profit
&lt;/h2&gt;

&lt;p&gt;US persons may receive Forms &lt;strong&gt;1099-DA&lt;/strong&gt;, &lt;strong&gt;1099-MISC&lt;/strong&gt;, and/or &lt;strong&gt;1099-B&lt;/strong&gt; by email from &lt;code&gt;no-reply@crypto.com&lt;/code&gt; (check spam). Crypto.com's FAQ for recent tax years states that &lt;strong&gt;cost basis is not required on Form 1099-DA&lt;/strong&gt; during the transitional period, and that they chose not to report basis to avoid unintended error.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proceeds on 1099-DA are not your taxable gain&lt;/li&gt;
&lt;li&gt;You still need acquisition cost for Form 8949 when the broker form is proceeds-only&lt;/li&gt;
&lt;li&gt;No 1099-MISC under the $600 rewards threshold does not automatically mean Earn / lockup / referral income is non-taxable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you also use Coinbase, keep each platform's CSVs separate. NutshellCrypto's &lt;a href="https://nutshellcrypto.com/coinbase-tax-report/" rel="noopener noreferrer"&gt;Coinbase tax report&lt;/a&gt; walkthrough covers the parallel 1099-DA pattern on that venue.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tiny App CSV sanity check
&lt;/h2&gt;

&lt;p&gt;Crypto.com Token Wallet CSV column names can vary by export year. Inspect the header row first. This sketch counts disposal-like rows without a usable basis field — a cheap red flag before you file.&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;csv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decimal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="n"&gt;DISPOSAL_HINTS&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;sell&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;trade&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;convert&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;exchange&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;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;or&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&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="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_token_wallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&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;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;missing_basis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;by_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;proceeds_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Decimal&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="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&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="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt; &lt;span class="ow"&gt;or&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&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;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&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;key&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&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="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Transaction Kind&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;Type&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;transaction_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;by_type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;DISPOSAL_HINTS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;proceeds_total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Native Amount&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;Amount (USD)&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;USD Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cost Basis&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;cost_basis_usd&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;Basis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;missing_basis&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;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;by_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by_type&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disposal_rows_missing_basis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;missing_basis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rough_proceeds_sum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proceeds_total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;scan_token_wallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crypto_com_token_wallet.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tune aliases against &lt;em&gt;your&lt;/em&gt; header row. This is not a tax engine — it catches empty basis fields and odd type mixes before they become Form 8949 mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import workflow that usually works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Export Token Wallet (plus Cash/Card if needed) for the tax year and prior years for basis.&lt;/li&gt;
&lt;li&gt;Export every relevant Exchange history set; split ranges if you hit caps.&lt;/li&gt;
&lt;li&gt;Save 1099 PDFs from Crypto.com's email link.&lt;/li&gt;
&lt;li&gt;Import into crypto tax software &lt;strong&gt;or&lt;/strong&gt; enter disposals on Form 8949 with your own basis where the broker form is proceeds-only.&lt;/li&gt;
&lt;li&gt;Report rewards income even when no 1099-MISC arrived; reconcile 1099-DA proceeds to your Form 8949 lines.&lt;/li&gt;
&lt;li&gt;Do not forget on-chain / DeFi Wallet activity outside Crypto.com's custodial App and Exchange.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Crypto.com's built-in Tax calculator is gone; &lt;strong&gt;CSVs + 1099s&lt;/strong&gt; are the workflow now.&lt;/li&gt;
&lt;li&gt;Export &lt;strong&gt;both App and Exchange&lt;/strong&gt; if you used both.&lt;/li&gt;
&lt;li&gt;Treat &lt;strong&gt;1099-DA proceeds as proceeds&lt;/strong&gt;, not profit — basis is still your job in the transitional years.&lt;/li&gt;
&lt;li&gt;Watch App download expiry, Exchange row/date caps, and rewards below the 1099-MISC threshold.&lt;/li&gt;
&lt;li&gt;Preserve inbound transfer basis across venues in a personal ledger or software profile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the full guide on NutshellCrypto: &lt;a href="https://nutshellcrypto.com/crypto-com-tax-report/" rel="noopener noreferrer"&gt;Crypto.com Tax Report: How to Download and Use It (2026)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not financial, tax, or investment advice. Educational only — rules depend on your facts and jurisdiction; talk to a qualified professional before filing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>python</category>
      <category>fintech</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
