<?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: Rafael Abuawad</title>
    <description>The latest articles on DEV Community by Rafael Abuawad (@rabuawad).</description>
    <link>https://dev.to/rabuawad</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%2F232437%2F0a8251cd-1b32-4675-8f6e-dc4fc2a23885.png</url>
      <title>DEV Community: Rafael Abuawad</title>
      <link>https://dev.to/rabuawad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rabuawad"/>
    <language>en</language>
    <item>
      <title>How to Implement the ERC-165 in Vyper</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Mon, 12 Oct 2026 19:57:22 +0000</pubDate>
      <link>https://dev.to/rabuawad/how-to-implement-the-erc-165-in-vyper-4i4l</link>
      <guid>https://dev.to/rabuawad/how-to-implement-the-erc-165-in-vyper-4i4l</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'preview'&lt;/p&gt;
</description>
      <category>ethereum</category>
      <category>web3</category>
      <category>solidity</category>
    </item>
    <item>
      <title>Polymarket and the oracle problem</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Mon, 12 Oct 2026 19:42:23 +0000</pubDate>
      <link>https://dev.to/rabuawad/polymarket-and-the-oracle-problem-5113</link>
      <guid>https://dev.to/rabuawad/polymarket-and-the-oracle-problem-5113</guid>
      <description>&lt;p&gt;When I first heard about Polymarket, I really liked the idea, and I still do. The fact that any future event can become tradable and, by doing so, people will put their money on what they TRULY believe will happen is actually pretty good, especially for large, important events.&lt;/p&gt;

&lt;p&gt;But most prediction markets, including Polymarket, have many flaws. The main one, and the most apparent, is the fact that the Oracle they use is not neutral.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an Oracle?
&lt;/h2&gt;

&lt;p&gt;If you are new to crypto or are not too deep into web3 lore, there is a problem called the Oracle Problem. This problem arises from the fact that blockchains are isolated systems, and getting data from the outside world is complicated, especially because you don't want to rely on a single entity to supply you with this information.&lt;/p&gt;

&lt;p&gt;A lot of products have raised millions of dollars in capital just to solve this issue. These products are known as Oracles, and their job is essentially to feed smart contracts data coming from external systems, like markets and events.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpuzvj0fdlas29yp9gda6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpuzvj0fdlas29yp9gda6.png" alt="Chainlink data-feeds diagram&lt;br&gt;
" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chainlink is a well-known one, providing price feeds and random numbers using a decentralized network of independent node operators. UMA tries to solve the oracle problem using tokenomics. UMA holders can dispute results by posting bonds at the optimistic-oracle layer, and staked UMA tokenholders vote when disputes escalate to the DVM. The core idea is simple: it is not in UMA holders' best interest to fake the data they feed to the oracle, because that would instead make the data coming from the oracle worthless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polymarket and Bias
&lt;/h2&gt;

&lt;p&gt;Here is where this becomes a theory, but what I think is happening on markets like "MicroStrategy sells Bitcoin by May 31st" is the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1cwk5gsp91nra0ks2lmz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1cwk5gsp91nra0ks2lmz.png" alt="@willo2_Poly post" width="600" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A lot of UMA holders position themselves deep in the most likely outcome. In this case, on the NO side of the trade. This is a simple trade: something that has a 99% chance of happening, and if you invest $1,000,000 into it, you can easily make more than ~$10,000 almost risk-free.&lt;/p&gt;

&lt;p&gt;Key word here: almost&lt;/p&gt;

&lt;p&gt;In the unlikely scenario that the event happens (or doesn't happen, depending on the market), the losses can be devastating, transferring millions of dollars from the market. What I think is happening is that a lot of UMA whales enter these trades in order to make a few mil on the side, almost risk-free, but when things don't go their way, they favor their original position at resolution time.&lt;/p&gt;

&lt;p&gt;A WSJ-cited analysis reported that in roughly 1 in 5 disputed bets, at least one voter had a direct financial interest.&lt;/p&gt;

&lt;p&gt;The MicroStrategy facts — Important nuance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy did sell 32 BTC&lt;/strong&gt; between May 26 and 31 (inside the deadline).&lt;/li&gt;
&lt;li&gt;The fight was whether the resolution requires public confirmation by May 31, not just the sale occurring.&lt;/li&gt;
&lt;li&gt;Polymarket posted additional context after the June 1 8-K, stating confirmation outside the window doesn't qualify.&lt;/li&gt;
&lt;li&gt;Final DVM vote reportedly backed NO with ~98.6% of voting power.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;In the MicroStrategy market, Strategy disclosed a sale that occurred before May 31, but Polymarket later clarified that confirmation outside the deadline would not count. The dispute then went to UMA tokenholders, who overwhelmingly ratified NO.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ethereum</category>
      <category>polymarket</category>
      <category>web3</category>
    </item>
    <item>
      <title>How to create your own cryptocurrency using Vyper and Python</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Thu, 08 Oct 2026 15:56:31 +0000</pubDate>
      <link>https://dev.to/rabuawad/how-to-create-your-own-cryptocurrency-using-vyper-and-python-5fab</link>
      <guid>https://dev.to/rabuawad/how-to-create-your-own-cryptocurrency-using-vyper-and-python-5fab</guid>
      <description>&lt;p&gt;Welcome. This article covers something very simple: how to create a cryptocurrency using &lt;a href="https://vyperlang.org/" rel="noopener noreferrer"&gt;Vyper&lt;/a&gt; and &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;. This guide is designed for beginners or enthusiasts who already know a bit about blockchain and want to dive deeper.&lt;/p&gt;

&lt;p&gt;This is not an introduction to programming (we won't cover basic variables or functions) or fundamental blockchain concepts. It assumes you already understand what a cryptocurrency and a wallet address are. By the end, we will have a cryptocurrency written in Vyper ready for the Arbitrum network.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Throughout this article, I will use the terms "cryptocurrency" and "ERC20" interchangeably.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What is a Cryptocurrency? What is an ERC20?
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;ERC20&lt;/strong&gt; is the technical standard used for smart contracts on the Ethereum Virtual Machine (EVM). &lt;a href="https://eips.ethereum.org/EIPS/eip-20" rel="noopener noreferrer"&gt;EIP-20&lt;/a&gt; defines the rules a contract must follow to be considered a cryptocurrency and be compatible with exchanges and wallets.&lt;/p&gt;

&lt;p&gt;Essentially, we need to implement 6 basic functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;totalSupply&lt;/code&gt;: The total number of coins in circulation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;balanceOf&lt;/code&gt;: Shows the balance of a specific address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transfer&lt;/code&gt;: Sends coins from the owner to a destination.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allowance&lt;/code&gt;: The amount a third party is permitted to move on behalf of the owner.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;approve&lt;/code&gt;: Authorizes an external address to move coins on behalf of the owner.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transferFrom&lt;/code&gt;: Allows an authorized address to execute the movement of funds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, we will implement 3 optional functions that improve the user experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: Name of the cryptocurrency (e.g., Bitcoin).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;symbol&lt;/code&gt;: Cryptocurrency symbol (e.g., BTC).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;decimals&lt;/code&gt;: Number of decimal places (usually 18).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;We will use a minimal setup with &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt; as a dependency manager and &lt;a href="https://titanoboa.readthedocs.io/" rel="noopener noreferrer"&gt;Titanoboa&lt;/a&gt; to compile Vyper.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install UV.&lt;/li&gt;
&lt;li&gt;Create the project: &lt;code&gt;uv init &amp;lt;project_name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a virtual environment: &lt;code&gt;uv venv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Activate the environment:&lt;/li&gt;
&lt;li&gt;    Linux/macOS: &lt;code&gt;source .venv/bin/activate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;    Windows: &lt;code&gt;.venv\Scripts\activate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install Titanoboa: &lt;code&gt;uv add titanoboa&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Project Structure&lt;br&gt;
📂 &lt;em&gt;contracts/&lt;/em&gt;: Where we will store the smart contract.&lt;br&gt;
📂 &lt;em&gt;scripts/&lt;/em&gt;: Python scripts for deployment.&lt;br&gt;
📂 &lt;em&gt;tests/&lt;/em&gt;: Unit tests for our ERC20.&lt;/p&gt;


&lt;h2&gt;
  
  
  Developing our Cryptocurrency
&lt;/h2&gt;

&lt;p&gt;Create the file &lt;em&gt;contracts/ERC20.vy&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pragma
&lt;/h3&gt;

&lt;p&gt;The pragma indicates which compiler version was used. We will use 0.4.3.&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;# pragma version ==0.4.3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing &lt;code&gt;totalSupply&lt;/code&gt; and Immutables
&lt;/h3&gt;

&lt;p&gt;We will define the name and symbol as state variables. For the total supply, we will use a public variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&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="n"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;totalSupply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@deploy&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;_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;_symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&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="n"&gt;self&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;_name&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;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_symbol&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;decimals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using &lt;code&gt;public(...)&lt;/code&gt;, Vyper automatically creates the getter function for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing balanceOf
&lt;/h3&gt;

&lt;p&gt;We will use a HashMap to track how much each wallet holds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 It is a good idea to mark internal variables with an _ (underscore), but by using public(), Vyper handles external visibility for us efficiently.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Implementing &lt;code&gt;transfer&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The transfer function is the heart of our coin. It allows moving value from point A to point B.&lt;/p&gt;

&lt;p&gt;In Vyper, the logic is straightforward: we subtract from the sender and add to the receiver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transfer&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;address&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;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Transfers tokens to a specified address.
    @param _to The address to transfer to.
    @param _value The amount to transfer.
    &lt;/span&gt;&lt;span class="sh"&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;balanceOf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;_value&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;balanceOf&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="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What is happening here?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@external&lt;/code&gt;: Allows anyone or any contract to call this function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;msg.sender&lt;/code&gt;: A global variable representing the address of whoever is executing the transaction.&lt;/li&gt;
&lt;li&gt;Extra security: Unlike older versions of other languages, Vyper natively handles overflow. If you try to send more than you have, the transaction automatically fails without extra code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Delegating Power: &lt;code&gt;approve&lt;/code&gt; and &lt;code&gt;transferFrom&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The concept of &lt;strong&gt;Allowance&lt;/strong&gt; is what makes ERC20s so powerful. It allows you to authorize an address (for example, a Decentralized Exchange) to withdraw a specific amount of tokens from your wallet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Allowance Mapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we need a structure to store who has permission to withdraw from whom. We will use a nested HashMap:&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;# Owner -&amp;gt; (Spends for me -&amp;gt; Amount)
&lt;/span&gt;&lt;span class="n"&gt;allowance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;approve&lt;/code&gt; Function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With this function, the user says: "I authorize this application to spend X amount of my tokens."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Authorizes `_spender` to transfer up to `_value` tokens.
    &lt;/span&gt;&lt;span class="sh"&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;allowance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;transferFrom&lt;/code&gt; Function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the function called by the authorized application. Note that here the sender is not the token owner, but the "spender".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transferFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Moves tokens from `_from` to `_to` using the allowance mechanism.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# If there is not enough permission, Vyper will throw an overflow error
&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;allowance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;

    &lt;span class="c1"&gt;# Move the balances
&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;balanceOf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;_value&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;balanceOf&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="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Events: The Final Touch
&lt;/h2&gt;

&lt;p&gt;For interfaces like MetaMask or Etherscan to show your transactions in real-time, you need &lt;strong&gt;Events&lt;/strong&gt;. Without them, the contract works, but it will be "invisible" to many applications.&lt;/p&gt;

&lt;p&gt;Add this to the top of your file, below the pragma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;


&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Approval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;spender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Make sure to call &lt;code&gt;log Transfer(...)&lt;/code&gt; and &lt;code&gt;log Approval(...)&lt;/code&gt; inside their respective functions so the standard is 100% official.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transfer&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="o"&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;_value&lt;/span&gt;&lt;span class="o"&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="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Approval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="o"&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="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transferFrom&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="o"&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;_value&lt;/span&gt;&lt;span class="o"&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="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a complete and functional ERC20 contract. It is minimalist, secure, and follows Vyper 0.4.3 best practices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Optimizations: Saving Gas with immutable
&lt;/h2&gt;

&lt;p&gt;If you are launching your coin on a network, every unit of gas counts. In the version we wrote above, the name, symbol, and decimals are saved in the blockchain's &lt;strong&gt;storage&lt;/strong&gt;. Reading from storage is one of the most expensive operations in terms of gas.&lt;/p&gt;

&lt;p&gt;For values that &lt;strong&gt;never change&lt;/strong&gt; after deploying the contract, Vyper offers the immutable wrapper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use immutable?
&lt;/h3&gt;

&lt;p&gt;By marking a variable as immutable, Vyper does not save it in expensive storage space. Instead, the value is embedded directly into the contract's bytecode during deployment. This makes reading this data extremely cheap (almost free).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Optimized Code
&lt;/h3&gt;

&lt;p&gt;Here is what the implementation looks like using immutables in Vyper 0.4.3:&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;# Declare immutable variables (UPPERCASE is recommended)
&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&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="n"&gt;SYMBOL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&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="n"&gt;DECIMALS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@deploy&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;_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&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="n"&gt;_symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&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="c1"&gt;# Assigned only once in the constructor
&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;_name&lt;/span&gt;
    &lt;span class="n"&gt;SYMBOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_symbol&lt;/span&gt;
    &lt;span class="n"&gt;DECIMALS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;


&lt;span class="c1"&gt;# Create external functions to comply with the ERC20 standard
&lt;/span&gt;&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="nd"&gt;@view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&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;String&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="nd"&gt;@view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;symbol&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;String&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;SYMBOL&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="nd"&gt;@view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decimals&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;uint8&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;DECIMALS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to use it?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;YES&lt;/strong&gt;: For the name, symbol, decimals, or an initial owner's address that will not change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NO&lt;/strong&gt;: For balances or any value you need to update in the future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mint Implementation&lt;/p&gt;

&lt;p&gt;We implement this logic by dividing it into parts: an immutable variable to know who the smart contract creator is, an external function for access control, and an internal one for accounting logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@deploy&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="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="n"&gt;OWNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mint&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;address&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;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only the OWNER can mint&lt;/span&gt;&lt;span class="sh"&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;_mint&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;_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@internal&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_mint&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;address&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;uint256&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;balanceOf&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="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&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;totalSupply&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="o"&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;_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key points of this implementation:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Control&lt;/strong&gt;: We use assert msg.sender == OWNER to ensure no one else can arbitrarily inflate the token supply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Supply Management&lt;/strong&gt;: Unlike a standard transfer, mint increases the self.totalSupply variable, maintaining global balance integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emission Convention&lt;/strong&gt;: Following best practices, we emit the Transfer event using empty(address) (the 0x00... address) as the origin. This indicates to block explorers that the tokens didn't come from another user but were just created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logic Separation&lt;/strong&gt;: Defining _mint as an @internal function is a recommended practice. It allows us to reuse the minting logic from other contract functions in future versions without repeating code or exposing access publicly.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Adding functions like mint doesn't break compatibility with the standard. The ERC20 defines a minimum mandatory interface; as long as those base functions exist and behave as expected, you can add all the extra logic you need.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Full Code (Vyper 0.4.3)
&lt;/h2&gt;

&lt;p&gt;Here is the final, optimized contract ready for deployment:&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;# pragma version ==0.4.3
&lt;/span&gt;

&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;


&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Approval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;_owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;


&lt;span class="c1"&gt;# Immutables
&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&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="n"&gt;SYMBOL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&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="n"&gt;DECIMALS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Storage Variables
&lt;/span&gt;&lt;span class="n"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;allowance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;span class="n"&gt;totalSupply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@deploy&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;_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&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="n"&gt;_symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&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="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;
    &lt;span class="n"&gt;SYMBOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_symbol&lt;/span&gt;
    &lt;span class="n"&gt;DECIMALS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
    &lt;span class="n"&gt;OWNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transfer&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;address&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;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Transfers tokens to a specified address.
    @param _to The address to transfer to.
    @param _value The amount to transfer.
    &lt;/span&gt;&lt;span class="sh"&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;balanceOf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;_value&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;balanceOf&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="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="o"&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;_value&lt;/span&gt;&lt;span class="o"&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="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Authorizes `_spender` to transfer up to `_value` tokens.
    &lt;/span&gt;&lt;span class="sh"&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;allowance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Approval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_spender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="o"&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="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transferFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Moves tokens from `_from` to `_to` using the allowance mechanism.
    &lt;/span&gt;&lt;span class="sh"&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;allowance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;_value&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;balanceOf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;_value&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;balanceOf&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="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="o"&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;_value&lt;/span&gt;&lt;span class="o"&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="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mint&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;address&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;uint256&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    @dev Mints `_value` amount of coins to `_to`
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only the OWNER can mint&lt;/span&gt;&lt;span class="sh"&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;_mint&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;_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="nd"&gt;@view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&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;String&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="nd"&gt;@view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;symbol&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;String&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;SYMBOL&lt;/span&gt;


&lt;span class="nd"&gt;@external&lt;/span&gt;
&lt;span class="nd"&gt;@view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decimals&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;uint8&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;DECIMALS&lt;/span&gt;


&lt;span class="nd"&gt;@internal&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_mint&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;address&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;uint256&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;balanceOf&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="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&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;totalSupply&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;_to&lt;/span&gt;&lt;span class="o"&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;_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Python Script: Blockchain Deployment
&lt;/h2&gt;

&lt;p&gt;For the final touch, we will use Titanoboa (or boa), an extremely lightweight and powerful framework for deploying and testing Vyper contracts using pure Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Script Setup
&lt;/h3&gt;

&lt;p&gt;Create a folder called scripts/ and, inside it, a file named &lt;em&gt;deploy.py&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Install eth-account to manage our keys securely: &lt;code&gt;uv add eth-account&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Security First: The Keystore
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Golden Rule&lt;/strong&gt;: Never leave your private key in plain text in your code or in &lt;code&gt;.env&lt;/code&gt; files. It's the easiest way to get your funds stolen.&lt;/p&gt;

&lt;p&gt;We will use a Keystore JSON file (a password-encrypted file). If you use tools like Ape or Foundry, you already have them; &lt;a href="https://x.com/rabuawad_/status/2036067875379663063" rel="noopener noreferrer"&gt;if not, make sure to generate one with eth-account.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Deployment Code
&lt;/h3&gt;

&lt;p&gt;Here is the complete script. It is configured for the Arbitrum Mainnet RPC, but you can change it to Sepolia if you prefer to test first.&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;getpass&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boa&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;eth_account&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_keystore_account&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Securely loads an account from a keystore file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account.keystore.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;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;as&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt; Enter your Keystore password: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;encrypted_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;account_pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encrypted_account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&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;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_pk&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Define the network (Arbitrum in this case)
&lt;/span&gt;    &lt;span class="c1"&gt;# You can use a provider like Alchemy/Infura if you want
&lt;/span&gt;    &lt;span class="n"&gt;rpc_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://arb1.arbitrum.io/rpc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;boa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_network_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpc_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 2. Load the account and add it to the boa environment
&lt;/span&gt;        &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_keystore_account&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;boa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&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;Deploying contract with account: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&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="c1"&gt;# 3. Deploy the contract
&lt;/span&gt;        &lt;span class="c1"&gt;# boa.load compiles and sends the deployment transaction
&lt;/span&gt;        &lt;span class="n"&gt;erc20_contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boa&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contracts/ERC20.vy&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;My Token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# _name
&lt;/span&gt;            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;           &lt;span class="c1"&gt;# _symbol
&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;Success! Contract deployed at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;erc20_contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&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;erc20_contract&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Launching the Token
&lt;/h3&gt;

&lt;p&gt;To execute it, simply run in your terminal: &lt;code&gt;uv run scripts/deploy.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Titanoboa will compile your Vyper code on the fly, sign the transaction with your encrypted account, and send it to the Arbitrum network.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>python</category>
    </item>
    <item>
      <title>Crypto is changing, and that is a good thing... In the long term</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Wed, 07 Oct 2026 23:22:11 +0000</pubDate>
      <link>https://dev.to/rabuawad/crypto-is-changing-and-that-is-a-good-thing-in-the-long-term-35ni</link>
      <guid>https://dev.to/rabuawad/crypto-is-changing-and-that-is-a-good-thing-in-the-long-term-35ni</guid>
      <description>&lt;h3&gt;
  
  
  Hackathons have changed completely.
&lt;/h3&gt;

&lt;p&gt;You are now most likely not competing for money but for the chance to make money in the long run. That is understandable: most hackathons were won by “bounty hunters,” people who did not want to build the product and only wanted a quick cash grab- literal bounty hunters. That hurt hackathons, and chains have realized there are few incentives left for hosting them. Now hackathons are focused on taking your idea (or MVP) to a real product that generates revenue and has users. That is a better approach in the long term because you reward people who are building real solutions and want to build for the long term, instead of people who build only to collect a check and say goodbye.&lt;/p&gt;




&lt;h3&gt;
  
  
  Grants have disappeared.
&lt;/h3&gt;

&lt;p&gt;Most grant programs have disappeared completely, but many of the chains that gave those grants have not stopped making revenue, so the funds are still there. What I imagine is that grants will focus on key tooling (compilers, frameworks, etc.) and on already existing protocols, which makes a lot of sense. Grants were used to bootstrap ideas; how many successful ideas did they bootstrap? A really small number. Now you have to put yourself out there first, and then you can get the money to keep going, which rewards builders in the long term.&lt;/p&gt;




&lt;h3&gt;
  
  
  Blockchains are (finally) becoming just part of the stack, not the unique selling point.
&lt;/h3&gt;

&lt;p&gt;Let me explain: in the early 2020s, protocols were valued at millions (millions!!) just because they were the only ones doing X on Y blockchain. That was misguided. Blockchains should always have been an implementation detail, not the USP. For example, look at something like Polymarket: do people using Polymarket know they are on Polygon? No. Most of them do not even know they are using a blockchain, and that was the end goal all along. The blockchain should just be part of the stack. Users do not care which blockchain you are building on; they care about the product. You (the developer) are the one who should care, but the end user should not. Good examples of this are Hyperliquid, Polymarket, and USDT.&lt;/p&gt;




&lt;h3&gt;
  
  
  Liquidity is going to concentrate.
&lt;/h3&gt;

&lt;p&gt;In the next couple of years, we are going to see a lot of protocols and chains close and wind down. That is a good thing in the long term, but it is going to signal a new age in crypto. Protocols with no purpose or use case are going to become irrelevant, which, if you think about it, is exactly what happens in every industry. For example, I never saw the use case for POAPs. A lot of people seem shaken by the news of them discontinuing the product, but in reality, the product was overvalued.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Why "create a wallet" is where most people quit crypto</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Wed, 07 Oct 2026 23:00:12 +0000</pubDate>
      <link>https://dev.to/rabuawad/why-create-a-wallet-is-where-most-people-quit-crypto-1ag2</link>
      <guid>https://dev.to/rabuawad/why-create-a-wallet-is-where-most-people-quit-crypto-1ag2</guid>
      <description>&lt;p&gt;Ask a new user to "create an account" and they know what to do. Ask them to "create a wallet" and you've already lost half of them.&lt;/p&gt;

&lt;p&gt;I've worked in Web3 for the last five years as a full-stack and smart contract developer at startups and protocols. I've given talks to both technical and non-technical audiences, and one theme keeps coming up: &lt;strong&gt;Web3 is extremely complicated for 99% of people.&lt;/strong&gt; The trouble starts on the very first screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  An account is not a wallet
&lt;/h3&gt;

&lt;p&gt;Every app people use has accounts. You sign up with your email, you forget your password, you click "reset password," and you're back in. Nobody thinks twice about it.&lt;/p&gt;

&lt;p&gt;A crypto wallet works differently. There's no "forgot password" button. You get a &lt;strong&gt;seed phrase&lt;/strong&gt;, which is 12 or 24 random words that you're told to write on paper and never show anyone. If you lose those words, or the private key behind them, your money is gone. Forever. No support team can get it back.&lt;/p&gt;

&lt;p&gt;For someone who just wants to receive a payment, that's terrifying. It also opens the door to scams: a fake "support agent" asking for your seed phrase is one of the most common ways people get drained.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-custody is great
&lt;/h3&gt;

&lt;p&gt;Self-custody has real benefits, and people who want it should have it. But making it the &lt;em&gt;entry point&lt;/em&gt; means most people never get past step one. They don't want to manage keys. They want to log in and get to their money.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✨🍉 &lt;strong&gt;Sandia lets you log in with your email. Simple as that.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use it now at: &lt;a href="https://sandiapay.app/" rel="noopener noreferrer"&gt;sandiapay.app&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  About Sandia
&lt;/h2&gt;

&lt;p&gt;Sandia started with a simple question: &lt;strong&gt;what if using Web3 wasn't complicated?&lt;/strong&gt; Log in with your email and get all the good parts of Web3 (stablecoins, yield, tokenized stocks) without the headaches.&lt;/p&gt;

&lt;p&gt;Sandia is live, and every feature is free. It's under active development, so if you find a bug, want a feature, or have a suggestion, I'd love to hear it.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.sandiapay.app/" rel="noopener noreferrer"&gt;sandiapay.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is part of the **What's broken in Web3&lt;/em&gt;* series:*&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Why "create a wallet" is where most people quit crypto&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"So I need ETH to move my USDT?" The gas fee problem&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Oh. That's on Ultraminion Chain." Why crypto payments keep getting lost&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;DeFi shouldn't need three approvals and a scam detector&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>web3</category>
      <category>cryptocurrency</category>
      <category>ethereum</category>
      <category>ux</category>
    </item>
    <item>
      <title>Web3 Security: How to Protect Your Private Key using Python</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:14:15 +0000</pubDate>
      <link>https://dev.to/rabuawad/web3-security-how-to-protect-your-private-key-using-python-5hj</link>
      <guid>https://dev.to/rabuawad/web3-security-how-to-protect-your-private-key-using-python-5hj</guid>
      <description>&lt;p&gt;In Smart Contract development, the most common (and costly) mistake is leaving private keys in plaintext within .env files or directly in the code. If accidentally pushed to GitHub, your funds will disappear in seconds.&lt;/p&gt;

&lt;p&gt;The professional solution is to use a &lt;strong&gt;Keystore JSON&lt;/strong&gt;: an encrypted file that protects your key with a password. This guide explains how to create a Keystore manager using Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;We will use &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt;, the fastest Python package manager, and the official Ethereum Foundation library.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initialize the project: &lt;code&gt;uv init keystore_manager&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install dependencies: &lt;code&gt;uv add eth-account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create the file: &lt;code&gt;touch manager.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Code: Keystore Manager
&lt;/h2&gt;

&lt;p&gt;The script prompts for your private key and a password to generate an encrypted 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;getpass&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;eth_account&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="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;/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/&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;/*                      KEYSTORE MANAGER                      */&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;/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;private_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; Enter your private key: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; Enter a password: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;password_confirm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Confirm your password: &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;password&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;password_confirm&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; ---ERROR &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Passwords do not match&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;loaded_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json_key_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loaded_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account.keystore.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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fp&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;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_key_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; Keystore created successfully&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="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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Security Points
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;getpass&lt;/strong&gt;: Essential for terminal scripts. It prevents your private key or password from appearing in console history or being visible to onlookers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account.encrypt&lt;/strong&gt;: Uses the Ethereum encryption standard. This generates a file that can only be decrypted with the correct password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.gitignore&lt;/strong&gt;: Even if encrypted, never upload these files to public repositories. Add *.json or your specific keystore filename to your .gitignore.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;To run your manager, execute:&lt;br&gt;
&lt;code&gt;uv run manager.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once the &lt;em&gt;account.keystore.json&lt;/em&gt; file is generated, you can securely use it in your deployment scripts with Titanoboa, Ape, or Foundry.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>Onchain experiments, and why we need them</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Tue, 06 Oct 2026 15:55:35 +0000</pubDate>
      <link>https://dev.to/rabuawad/onchain-experiments-and-why-we-need-them-21nk</link>
      <guid>https://dev.to/rabuawad/onchain-experiments-and-why-we-need-them-21nk</guid>
      <description>&lt;p&gt;The blockchain industry should never go stale, but something’s off. What used to feel wide open is getting squeezed by prediction markets and memecoins. That can’t be the whole story.&lt;/p&gt;

&lt;p&gt;When I first ran into Web3, CryptoKitties were a thing, NFTs were everywhere, and DeFi was exploding. You could seriously imagine building the next decentralized stablecoin, the next onchain collectible, the next lending protocol, with millions of people actually using the chain.&lt;/p&gt;

&lt;p&gt;Then a lot changed. Thousands of protocols never shipped. Thousands more faded away. And many of the same names that have been here forever seem to have stopped pushing new ideas.&lt;/p&gt;

&lt;p&gt;People stopped caring, but it doesn’t have to stay that way. We can still build a better future for blockchains; we just need new onchain experiences: less speculation, more real use cases, more things that hold up over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developers need a web3-first mindset.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What I mean: build truly permissionless, trustless protocols- protocols anyone can extend and compose on without an API key- a better use of blockchain tech. Onchain experiments need to feel exciting again. We need novel DeFi, new onchain games, etc. And goes without saying, we should also revisit older ideas that faded because of tech limits or market cycles. Bring that energy back.&lt;/p&gt;

&lt;p&gt;Projects like COINS by &lt;a href="https://x.com/z0r0zzz" rel="noopener noreferrer"&gt;@z0r0zzz&lt;/a&gt; are exactly the kind of direction that’s interesting and has real room to grow.&lt;/p&gt;

&lt;p&gt;We should build applications people want to use, applications that use what blockchains are uniquely good at. The old excuse list is shorter now: many technical limitations are gone, and we have the tooling to make UX genuinely smooth.&lt;/p&gt;

&lt;p&gt;How much value have something like ve(3,3) &amp;amp; Solidly added to DeFi? Something like Aave? Something like Uniswap Hooks? We need that... but times ten.&lt;/p&gt;

&lt;p&gt;I’ll go further: we should bring decentralized algorithmic stablecoins back into the conversation; the current landscape still isn’t good enough.&lt;/p&gt;

&lt;p&gt;To be clear: I’m not telling anyone to dump their life savings into a random DeFi protocol coded in a basement with two fever dreams and a chatbot.&lt;/p&gt;

&lt;p&gt;I am saying we should give serious experiments a fair shot, because something real can still come out of them.&lt;/p&gt;

&lt;p&gt;The industry has centered itself on speculation. Crypto can do more, and even where speculation exists, we can still aim for setups that are more decentralized, more trustless, with stronger game theory, and less extractive.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TLDR: Make onchain experiments cool again. Build with a web3-first developer mentality. Explore new ideas, and revive old ones worth another look.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Rethinking Web3 Development</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Tue, 06 Oct 2026 15:31:16 +0000</pubDate>
      <link>https://dev.to/rabuawad/rethinking-web3-development-11dl</link>
      <guid>https://dev.to/rabuawad/rethinking-web3-development-11dl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Why Most Devs Are Missing the Point&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem here is that 80% of developers working in the industry have no idea how to think in terms of blockchain tech. What I've seen is that most developers use the blockchain as a replacement for their backend and their database, turning the blockchain into the most expensive database in the world. This, for me, is a problem not only because most developers are not using any of the virtues of blockchain tech, but because this is stopping developers from innovating and coming up with new ideas. And for most developers, it's difficult to think in terms outside of Web2 or databases. In this article, I want to go over how one can start thinking in terms of Web3 and make better use of the technology in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do I Mean by Web3 Thinking?
&lt;/h2&gt;

&lt;p&gt;Now, what do I mean by Web3 thinking? Let's start with some examples to illustrate the shift from traditional approaches to ones that leverage blockchain's unique properties like decentralization, permissionlessness, and composability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: User Login and Profiles
&lt;/h3&gt;

&lt;p&gt;Let's say you've created an application that requires users to create a profile in order to access it. I'm going to give you a few seconds to think about how you can do that using Solidity... Well, most people come up with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

contract Pixelmon {
    struct User {
        string username;
        string bio;
    }

    mapping (uint256 _id =&amp;gt; User _user) public users;

    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They pass that to a mapping, and after that you have an indexed mapping that maps each user to an ID. Is this inherently bad? No, but it doesn't benefit from working on a blockchain. It doesn't benefit from having a decentralized ledger and autonomous code keeping it working.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What would be a better way of registering users?&lt;/strong&gt; How about a soulbound NFT with onchain dynamic metadata-something that can easily identify the user and sell as a benefit from at least some of the properties of the blockchain**. This way, the profile becomes transferable (or not, if soulbound), verifiable across chains, and composable with other apps. For instance, imagine linking it to DeFi protocols where the NFT could accrue reputation or rewards based on user activity, all without a centralized database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="n"&gt;pragma&lt;/span&gt; &lt;span class="n"&gt;solidity&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="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ERC721&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="s"&gt;"@openzeppelin/contracts/token/ERC721/ERC721.sol"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="n"&gt;PixelmonUser&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ERC721&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;ERC721&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PixelmonUser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"PIX"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;tokenURI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... Dynamic profile&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 2: Building a Betting Application
&lt;/h3&gt;

&lt;p&gt;Here's another example: Let's say you are creating a betting application. How do you represent a bet? Most people are going to create a struct similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

contract Betting {
    struct Bet {
        string description;
        uint256 in_favor;
        uint256 against;
        // ...
    }

    mapping(uint256 _index =&amp;gt; Bet _bet) public bets;
    mapping(uint256 =&amp;gt; mapping(address =&amp;gt; uint256)) public betAmounts;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And attach some mapping to say who voted for whom, and call it a day. That is not good.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What could have been done instead?&lt;/strong&gt; What about a pool with two tokens, one ERC1155: one represents the IN FAVOR side of the bet and the other one represents the AGAINST side. The bet becomes something more, something other people can build upon and even use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="n"&gt;pragma&lt;/span&gt; &lt;span class="n"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8.27&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"@openzeppelin/contracts/token/ERC1155/ERC1155.sol"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="n"&gt;Betting&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ERC1155&lt;/span&gt;
    &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;IN_FAVOR&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="n"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;AGAINST&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;function&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;tokenId&lt;/span&gt; &lt;span class="cm"&gt;/* ... more params */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It becomes permissionless.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, users could trade these tokens on a DEX, creating secondary markets around the bet’s outcome. Oracles could resolve the bet autonomously, distributing winnings based on token holdings. This turns a simple bet into a composable financial instrument that integrates with the broader Web3 ecosystem, rather than just a siloed entry in a smart contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does this matter?&lt;/strong&gt; Because this kind of thinking, leveraging blockchain’s unique properties like permissionlessness and composability, unlocks innovation. Instead of rebuilding Web2 apps onchain, we’re creating systems that are open, interoperable, and extensible by anyone in the ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Are Devs Missing This?
&lt;/h3&gt;

&lt;p&gt;So why do we keep seeing the same old designs, smart contracts that mimic SQL databases or centralized apps with a blockchain sticker slapped on?  Two reasons stand out. First, most developers are stuck in a Web2 mindset, trained to think in terms of SQL databases and front-end frameworks. Second, and this is the kicker, many Web3 developers don’t actually use Web3 themselves.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Issue: Devs Not Eating Their Own Medicine
&lt;/h2&gt;

&lt;p&gt;I’m not talking about the 1% of smart contract devs you see flexing on CT. I’m talking about the average developer who landed a job at a crypto company but whose only “crypto” experience is buying BTC on Coinbase or trading on Binance.&lt;/p&gt;

&lt;p&gt;I’ve heard stories, too many stories, of developers who didn’t even know how to send USDC. No shade, but how do you innovate in a space you don’t live in?&lt;/p&gt;

&lt;p&gt;This is why we’re stuck. If you’ve never swapped on a DEX, minted an NFT, or voted in a DAO, how can you dream up the next big thing? To break out of this rut, devs need to dive into the ecosystem headfirst. Only then will we stop building glorified databases and start pushing Web3 forward.&lt;/p&gt;

&lt;p&gt;To fix this, you need to dive in and try to think in terms in terms of Web3. Then can see the tech's true potential.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>solidity</category>
    </item>
    <item>
      <title>Web3: The "Ghost Chains" Problem and the Token Bubble</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Tue, 06 Oct 2026 14:55:04 +0000</pubDate>
      <link>https://dev.to/rabuawad/web3-the-ghost-chains-problem-and-the-token-bubble-44j9</link>
      <guid>https://dev.to/rabuawad/web3-the-ghost-chains-problem-and-the-token-bubble-44j9</guid>
      <description>&lt;p&gt;Why most Web3 products still look like smoke and mirrors, and what we (as developers) can do to solve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Problem: Multi-million Dollar Solutions for Non-existent Problems
&lt;/h2&gt;

&lt;p&gt;The biggest failure of Web3 today is that we have solutions looking for problems, or multi-million dollar infrastructures that do not resolve real needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn627840fjmgo4lv19g80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn627840fjmgo4lv19g80.png" alt="defillama.com/chain/cardano" width="525" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A clear example is the saturation of L1 Blockchains without users. You only need to look at the data to notice the disconnect: blockchains valued at billions with minuscule on-chain activity and fee generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Solution: Apps &amp;gt; Infrastructure
&lt;/h2&gt;

&lt;p&gt;As developers, we must shift our focus. The market doesn't need another L1 or another L2; it needs applications.&lt;/p&gt;

&lt;p&gt;99.9% of use cases can be perfectly resolved on the current EVM or SVM. The infrastructure already exists. Our job now is to build products that people want and can use, whether for Web3 veterans or the common user.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Token Paradox: Towards "Desensitization"
&lt;/h2&gt;

&lt;p&gt;Here is where I have an unpopular opinion: we need more tokens, not fewer.&lt;/p&gt;

&lt;p&gt;We need to reach a saturation point, similar to the dot-com bubble. In the 90s, having a website absurdly inflated a company's valuation, until the bubble burst and we understood that a website is a tool, not a guarantee of value.&lt;/p&gt;

&lt;p&gt;The same must happen with tokens. We should generate millions a day, just as we generate HTML, videos, or posts. By flooding the market, we desensitize people: a project having a token shouldn't mean it has intrinsic financial value. Only then will we cleanse the industry of empty speculation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7lpknny7t0mf6szdjchd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7lpknny7t0mf6szdjchd.jpeg" alt="coingecko.com/coins/zora" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last year was the turning point. If you were active in crypto, you'll remember the memecoin fever, where even the President of the US launched his own. That saturation without intrinsic value was necessary: it taught the market that having a token guarantees no value and that the barrier to entry for creating magical money is null.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Building Pasanaku: From Trusted to Trustless</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Mon, 21 Sep 2026 15:51:55 +0000</pubDate>
      <link>https://dev.to/rabuawad/building-pasanaku-from-trusted-to-trustless-2g20</link>
      <guid>https://dev.to/rabuawad/building-pasanaku-from-trusted-to-trustless-2g20</guid>
      <description>&lt;p&gt;Putting &lt;a href="https://www.pasanaku.fun" rel="noopener noreferrer"&gt;pasanaku.fun&lt;/a&gt; onchain and online has been an amazing journey, and it is a journey that is still ongoing.&lt;/p&gt;

&lt;p&gt;The protocol has gone through many, many iterations. I’m grateful for all of them, because I learned a lot while trying to build the perfect onchain rotating savings mechanism.&lt;/p&gt;

&lt;p&gt;In this article, I want to go over Pasanaku’s development journey so far: the decisions I made, how I built it, what I learned, and, most importantly, what comes next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to create something similar, whether you need smart contracts or the full stack, please feel free to connect with me on X (&lt;a href="https://x.com/rabuawad_" rel="noopener noreferrer"&gt;@rabuawad_&lt;/a&gt;).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But first, let me explain how Pasanaku works. It all started with a much simpler idea.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Pasanaku?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvzo5l9dg97x2tg6z60c3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvzo5l9dg97x2tg6z60c3.png" alt="How Pasanaku works" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pasanaku is a rotating savings pool. A group of people contributes a fixed amount on a schedule, and one participant receives the complete payout during each round.&lt;/p&gt;

&lt;p&gt;Traditional pasanakus depend on trust. You know the other participants, and everyone trusts that the people who receive an early payout will continue contributing to the later rounds.&lt;/p&gt;

&lt;p&gt;What I wanted to explore was simple: how much of this mechanism could be moved onchain? And, eventually, &lt;strong&gt;could a pasanaku work between people who did not know one another&lt;/strong&gt;?&lt;/p&gt;




&lt;h2&gt;
  
  
  Core v1: Putting a Trusted Pasanaku Onchain
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fefxx0dbzbk6g76cb40er.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fefxx0dbzbk6g76cb40er.png" alt="Old UI" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the first version, I wanted to build a simple rotating savings mechanism for trusted participants. You had to know the other people in the group, just like in a traditional rotating savings pool.&lt;/p&gt;

&lt;p&gt;Even with that trust assumption, Pasanaku let me explore some interesting uses of blockchain technology.&lt;/p&gt;

&lt;p&gt;The most interesting one was using ERC-1155 to represent each pasanaku. Every pool had a unique token ID. I liked this because it opened the possibility of making pasanakus composable.&lt;/p&gt;

&lt;p&gt;The first version was written in Vyper. It was a simple and efficient smart contract, and it consumed very little gas.&lt;/p&gt;

&lt;p&gt;Creating pasanakus was also simple. You created the pool and manually added every participant’s wallet. At launch, only a few currencies were supported: ETH, USDC, and USDT.&lt;/p&gt;

&lt;p&gt;Money distribution happened in two steps. Each participant deposited their contribution, and the participant assigned to that round claimed the payout.&lt;/p&gt;

&lt;p&gt;The contract used personal custody. It received the contributions and distributed the money, but it did not try to remove the trust assumptions of a traditional rotating savings pool.&lt;/p&gt;

&lt;p&gt;That was v1: a minimal and efficient way to put rotating savings onchain.&lt;/p&gt;

&lt;p&gt;At the time, I still did not understand how to build a trustless version of Pasanaku. I was also interested in another idea: dynamic NFTs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core v1.5: Trusted Pasanaku With Dynamic NFTs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flog4fvqrthnvlf5kt13k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flog4fvqrthnvlf5kt13k.png" alt="Onchain dynamic metadata concept" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this intermediate iteration, I ported everything to Solidity with one purpose in mind: adding dynamic, fully onchain metadata to the NFTs.&lt;/p&gt;

&lt;p&gt;I wanted to understand what was happening inside each pasanaku simply by looking at its NFT image.&lt;/p&gt;

&lt;p&gt;That took me down an interesting path. I developed a custom ERC-1155 onchain descriptor that read the pool’s state and generated the information needed to represent that pasanaku.&lt;/p&gt;

&lt;p&gt;The system was still trusted. You still needed to know every participant. The difference was that each NFT now had dynamically generated metadata showing what was happening inside its pool.&lt;/p&gt;

&lt;p&gt;I still think onchain metadata is extremely cool and useful. But I realized that, in this case, it did not add enough value to what the protocol already did.&lt;/p&gt;

&lt;p&gt;There was a more fundamental problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With the Trusted Model
&lt;/h2&gt;

&lt;p&gt;If you already know and trust every participant, do you really need a smart contract?&lt;/p&gt;

&lt;p&gt;You could simply send USDC directly to one another’s wallets without using an intermediary. Putting the process onchain made it more transparent, but it did not solve the hardest problem.&lt;/p&gt;

&lt;p&gt;The real question was whether Pasanaku could work between people who did not know or trust one another.&lt;/p&gt;

&lt;p&gt;That was when I started thinking about a new version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core v2: The Overcollateralization Breakthrough
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl76qcbwnwsdibt35f5gc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl76qcbwnwsdibt35f5gc.png" alt="Core v2" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For v2, I went back to Vyper. I kept ERC-1155 because I still wanted to enable composability between pasanakus in the future, and maybe even a secondary market.&lt;/p&gt;

&lt;p&gt;The model started to take shape when I realized that a completely trustless onchain rotating savings pool could work with one simple trick: &lt;strong&gt;overcollateralization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The first iteration of v2 was only an overcollateralized pasanaku. It worked through a factory where users could create pasanakus using supported currencies.&lt;/p&gt;

&lt;p&gt;The mechanism was simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, you supplied collateral.&lt;/li&gt;
&lt;li&gt;If you had enough collateral, you could join a pasanaku.&lt;/li&gt;
&lt;li&gt;Your collateral had to be worth more than the pool’s complete payout.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That was it.&lt;/p&gt;

&lt;p&gt;The model worked, but there was nothing particularly special about it yet. It was an overcollateralized pasanaku, and users had little incentive to participate beyond the savings mechanism itself.&lt;/p&gt;

&lt;p&gt;So I started working on an improvement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making the Collateral Productive
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2t7cy8u051ow9jfhthqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2t7cy8u051ow9jfhthqx.png" alt="Yield bearing collateral" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this iteration, I dropped support for multiple assets and focused on one: &lt;strong&gt;USDC&lt;/strong&gt;. I chose it because it is a widely used and widely available stablecoin.&lt;/p&gt;

&lt;p&gt;Then I realized something. Users already had to supply collateral, and that collateral was sitting inside the protocol. It could be doing something.&lt;/p&gt;

&lt;p&gt;So I decided to send that collateral to Fluid and make it productive.&lt;/p&gt;

&lt;p&gt;That led to what I think is Pasanaku’s most innovative mechanism, and, as far as I know, the only rotating savings pool algorithm designed to work this way in a decentralized system.&lt;/p&gt;

&lt;p&gt;The pool not only uses collateral to make participation between strangers possible. It also generates yield from that collateral, creating an additional game-theory incentive for people to participate.&lt;/p&gt;

&lt;p&gt;You can read more about the current mechanism on the &lt;a href="https://www.pasanaku.fun/about" rel="noopener noreferrer"&gt;Pasanaku About page&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Minimal Protocol
&lt;/h2&gt;

&lt;p&gt;With this version, I think I created something special and useful.&lt;/p&gt;

&lt;p&gt;I am planning several updates, but I do not plan to touch the smart contracts. What I wanted from the contract was something minimal and elegant, and I think that is what we have.&lt;/p&gt;

&lt;p&gt;Pasanaku is a single smart contract that is also a soulbound ERC-1155. It does what it is supposed to do: create a decentralized, trustless rotating savings pool for people who do not know one another.&lt;/p&gt;

&lt;p&gt;That was the idea, and I think the execution was excellent.&lt;/p&gt;

&lt;p&gt;The protocol also generates yield from the collateral, giving participants an additional reason to join and remain in the system. I think that is one of the most interesting parts of the architecture.&lt;/p&gt;

&lt;p&gt;If you have feedback about Pasanaku’s core architecture, please let me know. I will be more than happy to share my thoughts and hear yours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Frontend With Character
&lt;/h2&gt;

&lt;p&gt;Something really important, and something that does not get enough attention, especially in the age of AI engineering, is that the landing page and the UI have to be pristine.&lt;/p&gt;

&lt;p&gt;The old v1 interface is still online, and, well, I am not really proud of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It was built in only a few days with AI&lt;/strong&gt;. It was not necessarily a flop, but it lacked something. For me, it lacked essence. It did not have anything I could call unique.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8k4w4f614xftcl7j4cm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8k4w4f614xftcl7j4cm.png" alt="Pasanaku v1 OLD UI" width="799" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I mention this because an almost infinite number of AI-coded apps are reaching the market right now, and many of them feel the same. They look the same. They act the same.&lt;/p&gt;

&lt;p&gt;They often have the same bugs and the same missing features. By many definitions of the term, they are AI slop.&lt;/p&gt;

&lt;p&gt;The first version of the Pasanaku web app was probably somewhere in that territory. I built it for a hackathon, but I did not want the project to stay there.&lt;/p&gt;

&lt;p&gt;I have around 10 years of frontend development experience, and I wanted to put that experience into practice.&lt;/p&gt;

&lt;p&gt;For the v2 interface, I created a new design system. I also applied some of the most effective UI patterns available for Web3 applications.&lt;/p&gt;

&lt;p&gt;The result is, I think, a genuinely good interface with a unique essence. It is not a shoddy template. It is useful, readable, responsive, and fast. It has achieved a 99% Lighthouse score.&lt;/p&gt;

&lt;p&gt;The UI matters because it is how almost every user will experience the protocol. Almost no one is going to interact with Pasanaku directly through the smart contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpp9vm147ih728j96ikrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpp9vm147ih728j96ikrh.png" alt="Pasanaku new UI ✨" width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interface therefore has to make a fairly nuanced protocol feel understandable.&lt;/p&gt;

&lt;p&gt;It has to display collateral, current interest rates, available pasanakus, and a user’s active pasanakus. It also has to coordinate all the steps required for each onchain interaction.&lt;/p&gt;

&lt;p&gt;When someone creates a pool, the interface shows relevant active pasanakus so they can avoid paying a creation fee when a suitable pool already exists.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvq1jx83pmenxy74dm81b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvq1jx83pmenxy74dm81b.png" alt="Pasanaku new UI ✨ (Active pasanakus)" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of those pieces come together in an interface that I think is beautiful and, more importantly, useful.&lt;/p&gt;

&lt;p&gt;I am grateful that I was able to deliver it in record time. I learned a lot, especially about reading onchain data quickly and reliably.&lt;/p&gt;

&lt;p&gt;The frontend has features that you cannot create by simply prompting some code or copying a template. They required specialized knowledge of the protocol, Web3, and frontend development.&lt;/p&gt;

&lt;p&gt;I was also able to compress that knowledge into a repository that I think is really clean. That is another part of the project I am proud of.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;Pasanaku is online, and its core protocol is working, but the journey is not finished.&lt;/p&gt;

&lt;p&gt;The next updates will focus mainly on the product around the contract: making the experience clearer, improving how people discover and join pools, and continuing to refine the interface.&lt;/p&gt;

&lt;p&gt;The smart contract is intentionally minimal. I do not want to add complexity simply for the sake of adding features. The goal is to keep the protocol elegant while making the product around it better.&lt;/p&gt;

&lt;p&gt;There are also ideas I would still like to explore, including greater composability between Pasanaku and what a secondary market could eventually look like.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Building Pasanaku took me from a simple trusted savings pool to a protocol where strangers can save together through overcollateralization and productive collateral.&lt;/p&gt;

&lt;p&gt;Every version taught me something. V1 taught me how to represent rotating savings onchain. V1.5 pushed me into dynamic NFTs. V2 forced me to solve the actual trust problem.&lt;/p&gt;

&lt;p&gt;The frontend then forced me to make all of that complexity understandable and useful.&lt;/p&gt;

&lt;p&gt;I think we now have something minimal, elegant, and genuinely interesting. There is still a lot to improve, and that is exactly what makes the journey exciting.&lt;/p&gt;

&lt;p&gt;If you have feedback, want to discuss the architecture, or need help building something similar, from smart contracts to the full stack, please feel free to &lt;a href="https://x.com/rabuawad_" rel="noopener noreferrer"&gt;connect with me on X (@rabuawad_)&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Yul Dark Arts: Understanding the EVM from first principles</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Sun, 06 Sep 2026 22:54:04 +0000</pubDate>
      <link>https://dev.to/rabuawad/yul-dark-arts-understanding-the-evm-from-first-principles-11ja</link>
      <guid>https://dev.to/rabuawad/yul-dark-arts-understanding-the-evm-from-first-principles-11ja</guid>
      <description>&lt;p&gt;In this article, I want to walk you through my experience learning Yul to better understand how the EVM manages memory and how you can use this to your advantage, whether it is for learning more about the EVM or for trying to squeeze extra performance from a smart contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Yul?
&lt;/h2&gt;

&lt;p&gt;Yul is a type-ish, low-level intermediate representation where EVM opcodes are represented as simple function calls. The idea is to give the developer more control over memory management.&lt;/p&gt;

&lt;p&gt;Yul is highly flexible. Since you can use it directly inside Solidity, it has high levels of interoperability with Solidity, and since you are managing the low-level opcodes, it is faster (if you know what you are doing).&lt;/p&gt;

&lt;p&gt;Remember, Yul is like an EVM dialect, not a fully fledged programming language. This means that every data type is represented with &lt;code&gt;u256&lt;/code&gt;, which includes strings, bools, enums, etc. You are going to do everything manually: storage management, memory management, event emissions, manual &lt;code&gt;abi.encode&lt;/code&gt; and &lt;code&gt;abi.encodePacked&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you use Yul?
&lt;/h2&gt;

&lt;p&gt;The easiest way to use Yul is inside a Solidity smart contract, inside an &lt;code&gt;assembly&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;Yul is extremely interesting as a programming language, but it adds a lot of mental overhead. You need to understand this before moving forward. I do not recommend you code all your smart contracts in Yul. The main reason is that you need to pay extreme attention to memory, how to move and manage it. Solidity and Vyper already have tons of abstractions to achieve the same task. &lt;/p&gt;

&lt;p&gt;But I would recommend you use &lt;a href="https://vectorized.github.io/solady/" rel="noopener noreferrer"&gt;Solady&lt;/a&gt; if you are coding smart contracts in Solidity.&lt;/p&gt;

&lt;p&gt;For basic stuff, Yul, it can be straightforward, as we are going to see shortly. But as soon as the function at hand does more than just reading and modifying a few storage slots, it can get really tricky.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thinking in terms of slots
&lt;/h2&gt;

&lt;p&gt;In Yul, you stop thinking in variables and start thinking in slots. That shift mattered a lot for me. It changed how I looked at storage, and especially mappings, when writing other smart contracts using Vyper.&lt;/p&gt;

&lt;p&gt;Everything in contract storage is just a key-value pair: a slot holds a 32-byte word. You keep a mental map of those slots (and of memory, which is temporary and cheaper). Yes, it is that simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern A: Reading a single key
&lt;/h3&gt;

&lt;p&gt;To interact with a single value using Yul, we only need two opcodes, one to read and another to write. These opcodes are &lt;code&gt;sload&lt;/code&gt; and &lt;code&gt;sstore&lt;/code&gt; respectively. This pattern is the simplest one. You just need a storage slot and a value, and that is it.&lt;/p&gt;

&lt;p&gt;For example, let's say we want to read the &lt;code&gt;totalSupply&lt;/code&gt; of a super-optimized ERC20 (Solady-style namespaced slot). We would do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;constant&lt;/span&gt; &lt;span class="nx"&gt;_TOTAL_SUPPLY_SLOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x05345cdf77eb68f44c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;totalSupply&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_TOTAL_SUPPLY_SLOT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing is the same idea, just &lt;code&gt;sstore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_setTotalSupply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;newSupply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;sstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_TOTAL_SUPPLY_SLOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newSupply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you are minting and need to bump supply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_increaseTotalSupply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_TOTAL_SUPPLY_SLOT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;sstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_TOTAL_SUPPLY_SLOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pattern B: Reading a mapping
&lt;/h3&gt;

&lt;p&gt;Inside the EVM bytecode, there is no mapping per se. What there is is a pointer, a hashed key that points to a value. And mappings are no different. So, how do we calculate and use the concept of a mapping in Yul? Certainly, it is the most used data structure in all of EVM development, and the answer is actually really simple.&lt;/p&gt;

&lt;p&gt;Instead of storing a slot, we store a slot seed that, in conjunction with the other key in the mapping, will give you the actual slot where the mapping value lives. So, for example, we want to calculate the &lt;code&gt;balanceOf&lt;/code&gt; of a given user. We can do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;constant&lt;/span&gt; &lt;span class="nx"&gt;_BALANCE_SLOT_SEED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x87a211a2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_BALANCE_SLOT_SEED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;balanceSlot&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balanceSlot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing a balance is the same as math; then &lt;code&gt;sstore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_setBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_BALANCE_SLOT_SEED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;balanceSlot&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;sstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balanceSlot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why &lt;code&gt;mstore(0x0c, seed)&lt;/code&gt;  works
&lt;/h2&gt;

&lt;p&gt;This is the part that confused me the most at first, so let's slow down.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mstore&lt;/code&gt; is &lt;a href="https://www.geeksforgeeks.org/dsa/little-and-big-endian-mystery/" rel="noopener noreferrer"&gt;big-endian&lt;/a&gt;. When you write a 32-byte word at some offset, the most significant byte lands at that offset, and the least significant byte lands 31 bytes later. An &lt;code&gt;address&lt;/code&gt; is only 20 bytes, so when you &lt;code&gt;mstore(0x00, owner)&lt;/code&gt;  the address does not sit at &lt;code&gt;0x00&lt;/code&gt;. It sits at &lt;code&gt;mem[0x0c .. 0x1f]&lt;/code&gt;, after 12 bytes of zero padding.&lt;/p&gt;

&lt;p&gt;The Solady overlap trick uses that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mstore(0x0c, _BALANCE_SLOT_SEED)&lt;/code&gt; puts the 4-byte seed at the end of the word starting at &lt;code&gt;0x0c&lt;/code&gt;, which means the seed lands at &lt;code&gt;mem[0x28 .. 0x2b]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mstore(0x00, owner)&lt;/code&gt; puts the address at &lt;code&gt;mem[0x0c .. 0x1f]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;keccak256(0x0c, 0x20)&lt;/code&gt; hashes exactly 32 bytes: &lt;code&gt;[ address 20B | pad 8B | seed 4B ]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4zfviukziim1655dzvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4zfviukziim1655dzvv.png" alt="Oversimplified explanation of memory" width="800" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is why you start the hash at &lt;code&gt;0x0c&lt;/code&gt;, not at &lt;code&gt;0x00&lt;/code&gt;. You skip the left padding of the address word and pull the seed into the same 32-byte preimage.&lt;/p&gt;

&lt;p&gt;You will also see the packed form, which builds the same preimage with one &lt;code&gt;mstore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;owner_&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_BALANCE_SLOT_SEED&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;balanceSlot&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same hash input. Fewer stores. This is not the same thing as cleaning an address (more on that later).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern C: Reading a mapping of a mapping
&lt;/h3&gt;

&lt;p&gt;This pattern is also used a lot, in places like allowances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;constant&lt;/span&gt; &lt;span class="nx"&gt;_ALLOWANCE_SLOT_SEED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x7f5e9f20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;allowance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;spender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;spender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_ALLOWANCE_SLOT_SEED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allowanceSlot&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowanceSlot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the hash length is &lt;code&gt;0x34&lt;/code&gt; (52 bytes), not &lt;code&gt;0x20&lt;/code&gt;. You are hashing owner + seed + spender layout across more than one word. Writing is again the same slot, then &lt;code&gt;sstore(allowanceSlot, amount)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The seed pattern
&lt;/h2&gt;

&lt;p&gt;This is pretty self-explanatory. We use a seed in conjunction with the other values we want to use as a key, then hash them together to generate the actual key we want to use to store the specific value. Solady uses namespaced seeds like &lt;code&gt;_BALANCE_SLOT_SEED&lt;/code&gt; instead of compiler slots &lt;code&gt;0, 1, 2...&lt;/code&gt; so inherited contracts do not collide on storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing memory
&lt;/h2&gt;

&lt;p&gt;In libraries like Solady, you are going to see a lot of use of scratch space. Slots &lt;code&gt;0x00&lt;/code&gt; up to &lt;code&gt;0x3f&lt;/code&gt; are scratch memory, and you can essentially do whatever you want with them. You are mostly going to use memory to store values to calculate given slots, or to store (in memory) cleaned-up addresses. That is exactly why Patterns B and C hash in scratch: no free-memory allocation, just trash the first 64 bytes, hash, &lt;code&gt;sload&lt;/code&gt;/&lt;code&gt;sstore&lt;/code&gt;, done.&lt;/p&gt;

&lt;p&gt;The free memory pointer lives at &lt;code&gt;0x40&lt;/code&gt;. If you need real allocated memory (dynamic returns, bigger ABI payloads, building structs in memory), you read it first with &lt;code&gt;mload(0x40)&lt;/code&gt;, write from that pointer, then bump it so the next allocation does not overwrite you.&lt;br&gt;
The zero slot lives at &lt;code&gt;0x60&lt;/code&gt;. It is not recommended to write the zero slot, but if you ever need to do so, you have to set this slot back to &lt;code&gt;0&lt;/code&gt;. Solidity assumes that word stays zero.&lt;/p&gt;

&lt;p&gt;So the mental map is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0x00&lt;/code&gt; to &lt;code&gt;0x3f&lt;/code&gt;: scratch, safe to trash for hashing and temps (in carefully scoped assembly)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0x40&lt;/code&gt;: free memory pointer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0x60&lt;/code&gt;: zero slot, put it back if you touch it&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Footgun: cleaning addresses vs packing them&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two patterns look similar and are not the same.&lt;/p&gt;

&lt;p&gt;Cleaning forces the upper 12 bytes of an address word to zero before you compare it or emit it as a topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;shl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you skip this and some upper bits are dirty, &lt;code&gt;eq&lt;/code&gt; can fail against a clean address, or you can emit a weird topic that indexers will not match.&lt;/p&gt;

&lt;p&gt;Packing shifts the address left and &lt;code&gt;or&lt;/code&gt;s it with a seed so one &lt;code&gt;mstore&lt;/code&gt; builds the balance-slot preimage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;_BALANCE_SLOT_SEED&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cleaning is about the correctness of the address as an address. Packing is about building a storage key cheaply. Do not unify them in your head. I did. It bites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Events
&lt;/h2&gt;

&lt;p&gt;Since in Yul you are using plain EVM opcodes as functions, we need to compute the event signature and pass the parameters accordingly. If you don't know how events work under the hood, there is an amazing guide by RareSkills explaining how events work: &lt;a href="https://rareskills.io/post/ethereum-events" rel="noopener noreferrer"&gt;Solidity Events by RareSkills&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this article, we need to understand one thing: events can have up to 4 indexed topics, and topics are, in and of themselves, the data that the event is logging. The name of the event (its signature hash) is also considered a topic. An event can have a maximum of 3 indexed topics plus the signature topic. Essentially, we have to call the correct opcode. The opcodes to emit events are &lt;code&gt;log0&lt;/code&gt; through &lt;code&gt;log4&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Emitting a Transfer event in Solidity
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;indexed&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;indexed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Inside a function.&lt;/span&gt;
&lt;span class="nx"&gt;emit&lt;/span&gt; &lt;span class="nc"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Emitting a Transfer event in Solidity + Yul&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;constant&lt;/span&gt; &lt;span class="nx"&gt;_TRANSFER_EVENT_SIGNATURE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="mh"&gt;0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_emitTransfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;log3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_TRANSFER_EVENT_SIGNATURE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;log3&lt;/code&gt; means: signature topic + 2 indexed topics (&lt;code&gt;from&lt;/code&gt;, &lt;code&gt;to&lt;/code&gt;), and &lt;code&gt;amount&lt;/code&gt; lives in the non-indexed data payload. Same fingerprint idea as seeds: compute the event hash once, never &lt;code&gt;keccak&lt;/code&gt; the string at runtime.&lt;/p&gt;

&lt;p&gt;To compute the event signature, you can do the following operation in &lt;code&gt;chisel&lt;/code&gt; or &lt;code&gt;cast&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="sb"&gt;`&lt;/span&gt;keccak256&lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Transfer(address,address,uint256)"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom errors
&lt;/h2&gt;

&lt;p&gt;Custom errors in Yul follow a similar idea to events. You precompute the error selector (the first 4 bytes of &lt;code&gt;keccak256("ErrorName()")&lt;/code&gt;), store it in memory, then &lt;code&gt;revert&lt;/code&gt; with the right offset and size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// InsufficientBalance() selector = 0xf4d678b8&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_revertInsufficientBalance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="nx"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xf4d678b8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;revert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why &lt;code&gt;revert(0x1c, 0x04)&lt;/code&gt;? Because &lt;code&gt;mstore&lt;/code&gt; is big-endian again. A 4-byte selector written as a small integer lands at the end of the 32-byte word (&lt;code&gt;mem[0x1c .. 0x1f]&lt;/code&gt;). Offset &lt;code&gt;0x1c&lt;/code&gt; with length &lt;code&gt;0x04&lt;/code&gt; returns exactly those four bytes. No ABI fluff.&lt;/p&gt;

&lt;p&gt;If the error has arguments, you &lt;code&gt;mstore&lt;/code&gt; them after the selector and increase the revert size accordingly. Same mental model: fingerprint once, then speak in opcodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Precompute
&lt;/h2&gt;

&lt;p&gt;Something I learned is that many values can be precomputed using &lt;code&gt;chisel&lt;/code&gt; or &lt;code&gt;cast&lt;/code&gt; to save on compute. Solady does this a lot to squeeze the last drop of efficiency, making Solady's ERC20, for example, one of the cheapest ERC20s to interact with.&lt;br&gt;
Selectors, event signatures, slot seeds, bitmasks: if it never changes at runtime, do not compute them at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# event topic0&lt;/span&gt;
cast keccak &lt;span class="s2"&gt;"Transfer(address,address,uint256)"&lt;/span&gt;

&lt;span class="c"&gt;# error selector (first 4 bytes)&lt;/span&gt;
cast sig &lt;span class="s2"&gt;"InsufficientBalance()"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the result as a &lt;code&gt;uint256 private constant&lt;/code&gt; and you are done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Just to be clear, I don't think you should be writing Yul for smart contracts as your default. The whole point of this article is also the reason not to live there: you have to keep a mental map of how memory works, slots, scratch, free memory pointer, and dirty bits.&lt;/p&gt;

&lt;p&gt;That overhead is real.&lt;/p&gt;

&lt;p&gt;I have a lot of respect for the people behind many of the top protocols and libraries that squeeze the last drop of performance from their smart contracts using Yul, such as Solady and OpenSea, just to name a few. Learning to read that code is still worth it.&lt;br&gt;
If you want to learn from a true expert, I recommend you watch this video.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ew3pfnb2_V8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In my opinion, Solidity should improve the language; they are targeting that with &lt;a href="https://www.soliditylang.org/blog/2025/11/14/core-solidity-deep-dive/" rel="noopener noreferrer"&gt;Core Solidity&lt;/a&gt;, and I hope it goes well. The generated opcodes should be as efficient as possible, so developers are not forced into assembly for performance. Vyper has this right, and the resulting opcode is really good and well optimized. The high-level language does more of the hard work for you.&lt;/p&gt;

&lt;p&gt;Still, I don't think hand-rolled Yul is the way forward for writing regular smart contracts. That doesn't mean you can't become an expert in Yul, and Understanding Yul is a really good way to understand the EVM.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>cryptocurrency</category>
      <category>solidity</category>
    </item>
    <item>
      <title>Staking rewards are just an accumulator (Vyper 🐍)</title>
      <dc:creator>Rafael Abuawad</dc:creator>
      <pubDate>Wed, 02 Sep 2026 16:13:32 +0000</pubDate>
      <link>https://dev.to/rabuawad/staking-rewards-are-just-an-accumulator-in-vyper--48i2</link>
      <guid>https://dev.to/rabuawad/staking-rewards-are-just-an-accumulator-in-vyper--48i2</guid>
      <description>&lt;p&gt;Staking rewards look complicated. They are not.&lt;/p&gt;

&lt;p&gt;I went through a pile of articles on staking, and the mechanism is simpler than I expected. If you are going to ship a staking pool, you need to understand this model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The naive way
&lt;/h2&gt;

&lt;p&gt;First thought: an offchain bot that pays everyone every block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@bot.on_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blocks&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;distribute_rewards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&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;staker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stakers&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="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;staker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;calculate_rewards_amount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;staker&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.apeworx.io/silverback/latest/userguides/quickstart.html" rel="noopener noreferrer"&gt;Silverback&lt;/a&gt; can do this. It even looks clean.&lt;/p&gt;

&lt;p&gt;You will hit two walls. Gas: looping every staker every block is expensive. Liveness: if the bot goes down, stakers earn less than they were promised.&lt;/p&gt;

&lt;p&gt;There is a simpler way, and it lives onchain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy accounting
&lt;/h2&gt;

&lt;p&gt;Lazy accounting means you do not push rewards every second. You keep a running index of how much one staked token is owed, and you only settle a user when they touch the pool.&lt;/p&gt;

&lt;p&gt;The index is a counter that only goes up, from the start of the period to the end. From that one number you get two others: how much the user is owed, and a checkpoint (often called debt) of the index at the moment they staked.&lt;/p&gt;

&lt;p&gt;You are storing how much a single staked token would have earned since the beginning. Not everyone has been in since the beginning, so you also store each staker's checkpoint.&lt;/p&gt;

&lt;p&gt;Example. Rewards started at block 0. Bob deposits 100 tokens at block 10. The index is already 5. If you only stored &lt;code&gt;reward_per_token_staked&lt;/code&gt;, the contract would show Bob owed 500 for a stake he just opened. He is owed 0.&lt;/p&gt;

&lt;p&gt;So you store &lt;code&gt;reward_per_token_stored_paid&lt;/code&gt; for Bob, and you subtract it later. He does not get paid for time he was not in.&lt;/p&gt;

&lt;p&gt;That is the MasterChef idea, and it is small once you see it: track what one token has accrued since t=0, then store an offset per user because not everyone started at t=0. That offset is the reward debt.&lt;/p&gt;

&lt;p&gt;Every time someone interacts (stake, unstake, harvest), you update the index first. No active staker is cheated for time they were in. Time with zero stake isn't paid to the next depositor, and you don't pay the whole set every block.&lt;/p&gt;

&lt;p&gt;In this guide we implement it in Vyper, mostly from Unipool and the Synthetix staking rewards pattern. MasterChef and Unipool are not the same contract. I will mark where they split.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;I am using Moccasin, a Vyper-first framework built on top of Titanoboa. This repo's tests and invariants live under &lt;code&gt;tests/&lt;/code&gt; (unitary, integration, fuzz) — steal that pattern for your own protocol rather than inlining it here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Moccasin
&lt;/h3&gt;

&lt;p&gt;Python 3.11+ and uv. Install Moccasin as a global tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;moccasin
mox &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mox&lt;/code&gt; is the CLI. If it is missing, restart the terminal (or &lt;code&gt;source ~/.bashrc&lt;/code&gt;) after installing uv.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mox init staking-rewards
&lt;span class="nb"&gt;cd &lt;/span&gt;staking-rewards
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get &lt;code&gt;src/&lt;/code&gt;, &lt;code&gt;tests/&lt;/code&gt;, &lt;code&gt;script/&lt;/code&gt;, and &lt;code&gt;moccasin.toml&lt;/code&gt;. Put the contract in &lt;code&gt;src/StakingRewards.vy&lt;/code&gt;. This guide targets Vyper 0.4.3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add moccasin
uv add &lt;span class="s2"&gt;"vyper==0.4.3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Staking Rewards
&lt;/h2&gt;

&lt;p&gt;Vyper 0.4.3, with the compiler flag that puts a contract-wide reentrancy lock on external functions (this is also the 0.4 default).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# pragma version ==0.4.3
# pragma nonreentrancy on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two immutables: the token users stake, and the token you pay as rewards.&lt;/p&gt;

&lt;p&gt;In MasterChef, users stake LP tokens and the contract mints a separate reward token. In Unipool you transfer a pre-funded reward token, so the contract must already hold enough for the whole period. In this Unipool-style contract the two tokens must be different, otherwise harvest can pay out other people's deposits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev We import the built-in IERC20 interface.
from ethereum.ercs import IERC20

# @dev Immutable staking token set at deploy.
staking_token: public(immutable(IERC20))

# @dev Immutable rewards token set at deploy.
rewards_token: public(immutable(IERC20))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track how much &lt;code&gt;staking_token&lt;/code&gt; is in the pool, and how much each staker put in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev The total amount of staking_token staked.
total_supply: uint256

# @dev The amount that each individual staker
# has staked.
balances: HashMap[address, uint256]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the index, the reward rate, and the timestamps. &lt;code&gt;period_finish&lt;/code&gt; and &lt;code&gt;reward_rate&lt;/code&gt; are Unipool-specific, and immutables here because this implementation never changes them after deploy. We store how much each staked token has accrued, tokens per second for the whole pool, when the period ends, and the last time the pool was updated. Remember: we only update on deposit, withdraw, and harvest.&lt;/p&gt;

&lt;p&gt;Unipool keeps rewards separate. The user has to harvest to receive them. MasterChef pays on interaction: stake (if they already had a stake) or withdraw.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev We store when the staking rewards period is going to end.
#      Since there is no way to update this in this implementation we
#      are going to store this as an immutable.
period_finish: public(immutable(uint256))

# @dev Reward tokens distributed per second across the whole pool.
#      Since there is no way to update this in this implementation we
#      are going to store this as an immutable.
reward_rate: public(immutable(uint256))

# @dev We store the last time someone interacted with the pool.
last_update_time: public(uint256)

# @dev We store how much each individual staked token has earned in rewards
reward_per_token_stored: public(uint256)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per staker: the last index they were paid against, and rewards already owed but not harvested.&lt;/p&gt;

&lt;p&gt;If they harvested when &lt;code&gt;reward_per_token_stored&lt;/code&gt; was 3, and it is 7 now, those first 3 are done. &lt;code&gt;rewards&lt;/code&gt; is the snapshot of unclaimed earnings, so a later deposit or withdraw does not drop them if they have not harvested yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev Personal mark that each staker is going to have,
#      tracks what was the last `reward_per_token_stored`
#      that the user got paid
reward_per_token_stored_paid: public(HashMap[address, uint256])

# @dev Tracks how much rewards a user is owed.
rewards: public(HashMap[address, uint256])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contract only pays for 9 days:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev Duration in seconds. 9 days.
DURATION: constant(uint256) = 777600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a WAD scale for the accumulator. This is Q-notation precision for &lt;code&gt;reward_per_token_stored&lt;/code&gt;, not the token decimals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev Accumulator scale (1 WAD). This is Q-notation precision for
#      reward_per_token_stored, not the staking or rewards token decimals.
PRECISION: constant(uint256) = 10**18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;period_finish&lt;/code&gt;, &lt;code&gt;reward_rate&lt;/code&gt;, and &lt;code&gt;last_update_time&lt;/code&gt; in the constructor. Assert that the two tokens are different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@deploy
def __init__(
  _staking_token: IERC20,
  _rewards_token: IERC20,
  _reward_rate: uint256
):
  assert _staking_token != _rewards_token  # dev: staking and rewards tokens must differ
  staking_token = _staking_token
  rewards_token = _rewards_token
  period_finish = block.timestamp + DURATION
  reward_rate = _reward_rate
  self.last_update_time = block.timestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this contract's point of view, rewards run from deployment until 9 days later.&lt;/p&gt;

&lt;p&gt;Events you actually want in a production pool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# @dev Emitted when a user stakes tokens.
event Staked:
  user: indexed(address)
  amount: uint256

# @dev Emitted when a user withdraws staked tokens.
event Withdrawn:
  user: indexed(address)
  amount: uint256

# @dev Emitted when a user harvests staking rewards.
event RewardPaid:
  user: indexed(address)
  reward: uint256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The index
&lt;/h2&gt;

&lt;p&gt;The key function is &lt;code&gt;_reward_per_token()&lt;/code&gt;. It answers one question: how much has a single staked token earned?&lt;/p&gt;

&lt;p&gt;The formula is the same shape in Unipool and MasterChef. Unipool counts seconds. MasterChef counts blocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@internal
@view
def _reward_per_token() -&amp;gt; uint256:
  if self.total_supply == 0:
    return self.reward_per_token_stored

  return self.reward_per_token_stored + ((self._last_time_reward_applicable() - self.last_update_time) * reward_rate * PRECISION) // self.total_supply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;total_supply&lt;/code&gt; is 0, skip the accumulator. Nobody is there to credit, so that interval's rewards stay in the contract instead of going to whoever deposits next.&lt;/p&gt;

&lt;p&gt;Otherwise add &lt;code&gt;(time_delta * reward_rate * PRECISION) / total_supply&lt;/code&gt; to the stored index. Dilution is automatic: more stake in the pool, slower the index moves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_last_time_reward_applicable&lt;/code&gt; is just &lt;code&gt;min(block.timestamp, period_finish)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@internal
@view
def _last_time_reward_applicable() -&amp;gt; uint256:
  return min(block.timestamp, period_finish)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those two, you can compute unclaimed rewards for any staker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@internal
@view
def _earned(account: address) -&amp;gt; uint256:
  return self.balances[account] * (self._reward_per_token() - self.reward_per_token_stored_paid[account]) // PRECISION + self.rewards[account]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the function everything else hangs on: &lt;code&gt;_update_pool&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@internal
def _update_pool(account: address):
  self.reward_per_token_stored = self._reward_per_token()
  self.last_update_time = self._last_time_reward_applicable()
  if account != empty(address):
    self.rewards[account] = self._earned(account)
    self.reward_per_token_stored_paid[account] = self.reward_per_token_stored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call this before anything else, on every deposit, withdraw, and harvest. You settle the user against the current index before you change balances.&lt;/p&gt;

&lt;p&gt;Line 1 moves the global index forward: how much one token has earned since the start.&lt;/p&gt;

&lt;p&gt;Line 2 caps time at &lt;code&gt;period_finish&lt;/code&gt;, so you stop accruing after the window.&lt;/p&gt;

&lt;p&gt;If the account is not the zero address, you snapshot what they are owed into &lt;code&gt;rewards&lt;/code&gt;, then move their paid checkpoint up to the current index so the next delta does not double-count.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pool interactions
&lt;/h2&gt;

&lt;p&gt;Once &lt;code&gt;_update_pool&lt;/code&gt;, &lt;code&gt;_earned&lt;/code&gt;, and the index are right, that is the staking algorithm. The external functions are just the doors.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@external
def deposit(amount: uint256):
  assert amount &amp;gt; 0 # dev: invalid deposit amount
  self._update_pool(msg.sender)
  self.total_supply += amount
  self.balances[msg.sender] += amount
  extcall staking_token.transferFrom(msg.sender, self, amount)
  log Staked(user=msg.sender, amount=amount)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero amount reverts. Then update the pool before any balance change. Then bump &lt;code&gt;total_supply&lt;/code&gt; and the user's balance, transfer in (CEI), and emit &lt;code&gt;Staked&lt;/code&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@external
def withdraw(amount: uint256):
  assert amount &amp;gt; 0 # dev: invalid withdraw amount
  self._update_pool(msg.sender)
  self.total_supply -= amount
  self.balances[msg.sender] -= amount
  extcall staking_token.transfer(msg.sender, amount)
  log Withdrawn(user=msg.sender, amount=amount)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same order: check, update pool, then accounting, then transfer out. Vyper reverts on underflow, so you do not need a separate "enough stake" check. Notice we do not send rewards here. That is harvest.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@external
def harvest():
  self._update_pool(msg.sender)
  earned: uint256 = self._earned(msg.sender)
  if earned &amp;gt; 0:
    self.rewards[msg.sender] = 0
    extcall rewards_token.transfer(msg.sender, earned)
    log RewardPaid(user=msg.sender, reward=earned)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the pool first so &lt;code&gt;earned&lt;/code&gt; is current. If &lt;code&gt;earned&lt;/code&gt; is 0, do nothing (no event). Else zero &lt;code&gt;rewards&lt;/code&gt;, then transfer (CEI).&lt;/p&gt;

&lt;p&gt;You could add &lt;code&gt;exit()&lt;/code&gt; that withdraws and harvests in one call. If you want to write it, go for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra views
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@external
@view
def balanceOf(account: address) -&amp;gt; uint256:
    return self.balances[account]

@external
@view
def totalSupply() -&amp;gt; uint256:
    return self.total_supply

@external
@view
def last_time_reward_applicable() -&amp;gt; uint256:
    return self._last_time_reward_applicable()

@external
@view
def reward_per_token() -&amp;gt; uint256:
    return self._reward_per_token()

@external
@view
def earned(account: address) -&amp;gt; uint256:
    return self._earned(account)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;A few things you should not skip:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Be honest about funding. The pool has to hold enough &lt;code&gt;rewards_token&lt;/code&gt; for the whole period. If it does not, &lt;code&gt;harvest&lt;/code&gt; reverts, and that is a broken product, not a gas trick.&lt;/li&gt;
&lt;li&gt;This is Unipool, by k06a, not MasterChef. Read the original if you want the smallest version of the idea.&lt;/li&gt;
&lt;li&gt;Do the math once on paper. The &lt;a href="https://rareskills.io/post/staking-algorithm" rel="noopener noreferrer"&gt;RareSkills staking algorithm post&lt;/a&gt; is the cleanest path to this formula. Useful if you care about algorithmic thinking, not just copying the contract.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't only ship the pool. Understand the index.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/rafael-abuawad/staking-rewards" rel="noopener noreferrer"&gt;StakingRewards source and tests&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>smartcontract</category>
      <category>vyper</category>
    </item>
  </channel>
</rss>
