<?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: yo yo ben zhou</title>
    <description>The latest articles on DEV Community by yo yo ben zhou (@yo_yobenzhou_97e82d68eb).</description>
    <link>https://dev.to/yo_yobenzhou_97e82d68eb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2846174%2F651f115f-99c1-41d2-b121-8cf4868cf878.jpg</url>
      <title>DEV Community: yo yo ben zhou</title>
      <link>https://dev.to/yo_yobenzhou_97e82d68eb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yo_yobenzhou_97e82d68eb"/>
    <language>en</language>
    <item>
      <title>Optimizing Solidity Smart Contracts &amp; Hardhat Testing – MultiUserBank Edition</title>
      <dc:creator>yo yo ben zhou</dc:creator>
      <pubDate>Wed, 19 Feb 2025 06:10:13 +0000</pubDate>
      <link>https://dev.to/yo_yobenzhou_97e82d68eb/optimizing-solidity-smart-contracts-hardhat-testing-multiuserbank-edition-27le</link>
      <guid>https://dev.to/yo_yobenzhou_97e82d68eb/optimizing-solidity-smart-contracts-hardhat-testing-multiuserbank-edition-27le</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Introduction
&lt;/h2&gt;

&lt;p&gt;For today, I transformed my smart contract from a single-user bank to a multi-user bank, optimized gas costs, secured ETH transfers, and gained a deep understanding of reentrancy protection.  &lt;/p&gt;




&lt;h3&gt;
  
  
  Today's Highlights
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Implemented user-specific balances with &lt;code&gt;mapping(address =&amp;gt; uint)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Optimized storage access to reduce &lt;code&gt;SLOAD&lt;/code&gt; gas costs&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Replaced &lt;code&gt;.transfer()&lt;/code&gt; with &lt;code&gt;.call{value: amount}("")&lt;/code&gt; for secure ETH transfers&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Enhanced Hardhat test coverage with balance tracking&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Deployed contracts dynamically with &lt;code&gt;deploy.js&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Understood and mitigated Reentrancy Attacks!&lt;/strong&gt;  &lt;/p&gt;


&lt;h2&gt;
  
  
  1️⃣ Upgrading to Multi-User Bank
&lt;/h2&gt;
&lt;h3&gt;
  
  
  🔴 Problem: Single-User Balance Tracking
&lt;/h3&gt;

&lt;p&gt;Initially, my contract tracked &lt;strong&gt;one balance for the entire contract&lt;/strong&gt;, meaning all users &lt;strong&gt;shared the same balance&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint public balance; // Tracks one balance for all users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴 Issue: All users shared a single balance, making it unrealistic for a real banking system.  &lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Solution: Use a &lt;code&gt;mapping&lt;/code&gt; to Track Balances Per User
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(address =&amp;gt; uint) balance;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Each user now has their own balance&lt;br&gt;&lt;br&gt;
✅ Deposits and withdrawals are specific to each address&lt;/p&gt;

&lt;p&gt;Updated deposit function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function deposit() public payable {
    require(msg.value &amp;gt; 0, "Deposit money must be higher than zero");
    balance[msg.sender] += msg.value;
    emit Deposit(msg.sender, msg.value, balance[msg.sender]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Each user’s balance is updated independently! &lt;/p&gt;




&lt;h2&gt;
  
  
  2️⃣ Gas Optimization: Reduce Storage Reads (&lt;code&gt;SLOAD&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Storage reads (&lt;code&gt;SLOAD&lt;/code&gt;) in Solidity are &lt;strong&gt;expensive&lt;/strong&gt;, so minimizing them reduces gas costs.  &lt;/p&gt;

&lt;p&gt;🔴 Original code with redundant &lt;code&gt;SLOAD&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;uint current_balance = balance[msg.sender];  
require(amount &amp;lt;= current_balance, "Insufficient balance");  
balance[msg.sender] = current_balance - amount;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴 Problem:** Reads storage &lt;strong&gt;twice&lt;/strong&gt; (extra gas usage).  &lt;/p&gt;

&lt;p&gt;💡 Solution: Read from storage once, then modify in memory**&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint userBalance = balance[msg.sender];  
require(amount &amp;lt;= userBalance, "Insufficient balance");  
balance[msg.sender] = userBalance - amount;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Storage access reduced from two to one (saving gas)!  &lt;/p&gt;




&lt;h2&gt;
  
  
  3️⃣ Safer ETH Transfers: Using &lt;code&gt;.call{value: amount}("")&lt;/code&gt; Instead of &lt;code&gt;.transfer()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Initially, I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;payable(msg.sender).transfer(amount);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴 Problem:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.transfer()&lt;/code&gt; has a gas limit of 2300, which **may fail if the receiving address is a contract.
&lt;/li&gt;
&lt;li&gt;It does not return a success/failure flag, making debugging harder.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 Solution: Use &lt;code&gt;.call{value: amount}("")&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;(bool sent, ) = payable(msg.sender).call{value: amount}("");
require(sent, "Transfer failed");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Handles ETH transfers more safely! &lt;br&gt;
✅ Prevents unexpected failures due to gas limits!  &lt;/p&gt;


&lt;h2&gt;
  
  
  4️⃣ Reentrancy Protection: Why This Version is Safe
&lt;/h2&gt;

&lt;p&gt;I deepened my understanding of reentrancy vulnerabilities today!  &lt;/p&gt;
&lt;h3&gt;
  
  
  🔴 What is a Reentrancy Attack?
&lt;/h3&gt;

&lt;p&gt;A malicious contract can repeatedly call the &lt;code&gt;withdrawal&lt;/code&gt; function before the balance updates, draining the contract’s funds, we may extend this topic in the future posts with more detailed examples. &lt;/p&gt;
&lt;h3&gt;
  
  
  🚨 Vulnerable Code Pattern:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Transfer failed");
balance[msg.sender] -= amount;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;🔴 Problem: The contract &lt;strong&gt;sends ETH before updating the balance&lt;/strong&gt;, allowing reentrancy!  &lt;/p&gt;
&lt;h3&gt;
  
  
  💡 Solution: Update the Balance &lt;strong&gt;Before&lt;/strong&gt; Sending ETH
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint userBalance = balance[msg.sender];
require(amount &amp;lt;= userBalance, "Insufficient balance");

// ✅ Update balance FIRST before sending ETH
balance[msg.sender] = userBalance - amount;

(bool sent, ) = payable(msg.sender).call{value: amount}("");
require(sent, "Transfer failed");

emit Withdrawal(msg.sender, amount, balance[msg.sender]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ Reentrancy attack is impossible because the balance updates before ETH is sent!  &lt;/p&gt;


&lt;h2&gt;
  
  
  5️⃣ Writing and Running Hardhat Tests
&lt;/h2&gt;

&lt;p&gt;After improving the contract, I wrote comprehensive Hardhat tests.&lt;/p&gt;
&lt;h3&gt;
  
  
  Key Test Cases:
&lt;/h3&gt;

&lt;p&gt;✅ Deposits update the correct user’s balance&lt;br&gt;&lt;br&gt;
✅ Withdrawals succeed and update the user’s balance correctly&lt;br&gt;&lt;br&gt;
✅ Withdrawals fail when balance is insufficient&lt;br&gt;
✅ Gas fees are deducted correctly &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardhat Test File (multiUserBank.test.js):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MultiUserBank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bank&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;addr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addr2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addr2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Bank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MultiUserBank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;bank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForDeployment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should allow user-specific deposits&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserBankBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should allow withdrawals and update balances correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;beforeBankBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserBankBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;beforeEthBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withdrawal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gasUsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gasUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gasPrice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;afterBankBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserBankBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;afterBankBalance&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="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;beforeBankBalance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;afterEthBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;afterEthBalance&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="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;beforeEthBalance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;gasUsed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should prevent overdrafts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withdrawal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&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;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revertedWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Insufficient balance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Thorough tests ensure contract safety! &lt;br&gt;
✅ Validates ETH balance changes post-withdrawal! &lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Final Thoughts:
&lt;/h2&gt;

&lt;p&gt;Today’s Solidity deep dive helped me:&lt;br&gt;&lt;br&gt;
✅ Upgrade to a Multi-User Banking System using &lt;code&gt;mapping&lt;/code&gt;&lt;br&gt;
✅ Prevent Reentrancy Attacks by updating balances before sending ETH&lt;br&gt;&lt;br&gt;
✅ Reduce gas costs by optimizing storage access&lt;br&gt;&lt;br&gt;
✅ Deploy contracts dynamically instead of hardcoding deployments &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow my Solidity progress on GitHub, Dev.to, and Medium!&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;🌍 Join Me on This Journey!&lt;br&gt;
If you're also learning Solidity, Smart Contracts, or blockchain development, let's connect!&lt;br&gt;
📌GitHub: &lt;a href="https://github.com/benzdriver" rel="noopener noreferrer"&gt;https://github.com/benzdriver&lt;/a&gt;&lt;br&gt;
📌LinkedIn: &lt;a href="https://www.linkedin.com/in/ziyan-zhou/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/ziyan-zhou/&lt;/a&gt;&lt;br&gt;
💡 Let’s build the future of blockchain together! 🚀  &lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>solidity</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Solidity Learning Journey:  Deploying and Testing a Smart Contract</title>
      <dc:creator>yo yo ben zhou</dc:creator>
      <pubDate>Wed, 12 Feb 2025 19:36:59 +0000</pubDate>
      <link>https://dev.to/yo_yobenzhou_97e82d68eb/solidity-learning-journey-day-1-deploying-and-testing-a-smart-contract-2h7d</link>
      <guid>https://dev.to/yo_yobenzhou_97e82d68eb/solidity-learning-journey-day-1-deploying-and-testing-a-smart-contract-2h7d</guid>
      <description>&lt;h2&gt;
  
  
  🚀Previously in My Journey…
&lt;/h2&gt;

&lt;p&gt;In my previous post, I shared why I'm transitioning from SDET to Smart Contract Engineer. I talked about my fascination with Bitcoin's whitepaper, Ethereum's smart contracts, and the revolutionary potential of Web3.&lt;br&gt;
But motivation alone isn't enough - you have to put in the work.&lt;br&gt;
Today, I'm diving into Solidity for the first time, writing, deploying, and testing my first smart contract.&lt;/p&gt;


&lt;h3&gt;
  
  
  🎯The Plan: A Simple Ethereum Bank
&lt;/h3&gt;

&lt;p&gt;For my first Solidity project, I decided to build a basic Ethereum bank - a contract where users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏦 Deposit ETH into the contract.&lt;/li&gt;
&lt;li&gt;💸 Withdraw ETH from the contract.&lt;/li&gt;
&lt;li&gt;📊 Keep track of balances (but let Solidity handle the math).
Sounds simple, right? Well, things got complicated fast.&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  🛑 Solidity's First Surprise: Depositing ETH Works Differently Than I Thought
&lt;/h4&gt;

&lt;p&gt;Coming from traditional programming, I assumed I needed a function 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;function deposit(uint amount) public payable {
    require(amount &amp;gt; 0, "Deposit must be greater than zero");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❌ WRONG Solidity doesn't work like that.The ETH amount is automatically included in &lt;code&gt;msg.value&lt;/code&gt;, so no need to pass &lt;code&gt;amount&lt;/code&gt; explicitly. A function just needs the &lt;code&gt;payable&lt;/code&gt; modifier to receive ETH.&lt;/p&gt;

&lt;p&gt;💡 LESSON:Solidity handles ETH transfers at the transaction level, not as function parameters.&lt;/p&gt;




&lt;h4&gt;
  
  
  💰 Withdrawals and Solidity's Automatic Balance Tracking
&lt;/h4&gt;

&lt;p&gt;Next, I worked on withdrawals, assuming I needed a balance variable. But Solidity already tracks contract balances internally using address(this).balance.&lt;br&gt;
So, instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint public balance;
balance -= amount;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could just let Solidity handle the math and use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;payable(msg.sender).transfer(amount);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 LESSON: Solidity is smarter than I thought. You don't need to track balances manually - the blockchain does it for you.&lt;/p&gt;




&lt;h4&gt;
  
  
  🛠️ Deploying and Testing in Hardhat: "Why Isn't My Contract Deploying?"
&lt;/h4&gt;

&lt;p&gt;I decided to deploy my contract using Hardhat. I wrote a deploy.js script, but then realized:&lt;br&gt;
❌ WRONG. Hardhat tests don't actually use &lt;code&gt;deploy.js&lt;/code&gt;.&lt;br&gt;
✅ RIGHT Each test runs on a fresh blockchain state, deploying dynamically.&lt;br&gt;
Still, I ran into errors like:&lt;br&gt;
❌ &lt;code&gt;HH606: Solidity version mismatch&lt;/code&gt; → Fixed by updating &lt;code&gt;hardhat.config.js&lt;/code&gt; to include solidity version inside.&lt;br&gt;
❌ &lt;code&gt;"Cannot find module 'dotenv'"&lt;/code&gt; → Installed missing package (&lt;code&gt;dotenv&lt;/code&gt;).&lt;br&gt;
❌ &lt;code&gt;Hardhat compilation issues&lt;/code&gt; → Fixed by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat clean npx hardhat compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 LESSON: Hardhat automates a lot, but you have to configure Solidity versions correctly to avoid mismatches.&lt;/p&gt;




&lt;h4&gt;
  
  
  🔥 The Magic of Indexed Events in Solidity
&lt;/h4&gt;

&lt;p&gt;Solidity lets you log transactions with events, which I thought were just for debugging. ❌ WRONG.&lt;br&gt;
✅ Events actually live on the blockchain and make transaction searches super efficient.&lt;br&gt;
For example:&lt;br&gt;
&lt;code&gt;event Deposit(address indexed sender, uint amount);&lt;/code&gt;&lt;br&gt;
Indexed parameters make searching logs faster.&lt;br&gt;
Why not use &lt;code&gt;msg.sender&lt;/code&gt; directly? Solidity doesn't allow direct global variables in event definitions.&lt;/p&gt;

&lt;p&gt;So instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event Deposit(msg.sender, msg.value);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event Deposit(address indexed sender, uint amount);
emit Deposit(msg.sender, msg.value);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 LESSON: Events aren't just console logs - they are part of the Ethereum blockchain history.&lt;/p&gt;




&lt;p&gt;🔑 &lt;strong&gt;Key Takeaways from Day 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ Solidity has built-in ETH balance tracking - no need for manual balance variables.&lt;br&gt;
✅ &lt;strong&gt;payable&lt;/strong&gt; functions automatically receive ETH without an explicit amount parameter.&lt;br&gt;
✅ Hardhat automates testing but requires careful Solidity version configuration.&lt;br&gt;
✅ Events are stored on-chain and indexed makes them searchable.&lt;/p&gt;




&lt;p&gt;** 🚀 Next Steps &amp;amp; Future Improvements**&lt;/p&gt;

&lt;p&gt;🔹 Add multi-user banking using mapping(address =&amp;gt; uint) balances.&lt;br&gt;
🔹 Implement admin-only withdrawals for full contract balance management.&lt;br&gt;
🔹 Deploy SingleUserBank on a public Ethereum testnet.&lt;br&gt;
🔹 Explore gas optimizations and smart contract security best practices.&lt;/p&gt;

&lt;p&gt;🌍 Join Me on This Journey!&lt;br&gt;
If you're also learning Solidity, Smart Contracts, or blockchain development, let's connect!&lt;br&gt;
📌GitHub: &lt;a href="https://github.com/benzdriver" rel="noopener noreferrer"&gt;https://github.com/benzdriver&lt;/a&gt;&lt;br&gt;
📌LinkedIn: &lt;a href="https://www.linkedin.com/in/ziyan-zhou/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/ziyan-zhou/&lt;/a&gt;&lt;br&gt;
💡 Let’s build the future of blockchain together! 🚀&lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>solidity</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Solidity, Smart Contracts, and My Leap from SDET to Web3 Developer</title>
      <dc:creator>yo yo ben zhou</dc:creator>
      <pubDate>Tue, 11 Feb 2025 08:05:48 +0000</pubDate>
      <link>https://dev.to/yo_yobenzhou_97e82d68eb/from-sdetdevops-to-smart-contract-engineer-a-leap-into-the-future-of-blockchain-4pjj</link>
      <guid>https://dev.to/yo_yobenzhou_97e82d68eb/from-sdetdevops-to-smart-contract-engineer-a-leap-into-the-future-of-blockchain-4pjj</guid>
      <description>&lt;h3&gt;
  
  
  Why Am I Making the Switch?
&lt;/h3&gt;

&lt;p&gt;For years, I worked as a Software Development Engineer in Test (SDET)—a field deeply rooted in traditional software development. It was a stable career, but something was missing. That missing piece? &lt;strong&gt;A sense of true innovation and revolution&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Looking back, I think my journey toward &lt;strong&gt;becoming a Smart Contract Engineer&lt;/strong&gt; can be traced to one of my biggest passions: &lt;strong&gt;investing in cryptocurrency&lt;/strong&gt;.  &lt;/p&gt;

&lt;h3&gt;
  
  
  The Moment That Changed Everything
&lt;/h3&gt;

&lt;p&gt;Like many others, my crypto journey started with &lt;strong&gt;Bitcoin&lt;/strong&gt;, the first truly decentralized digital currency. At its core, Bitcoin wasn’t just another asset—it was a fundamental shift in the way we think about money, trust, and decentralization.  &lt;/p&gt;

&lt;p&gt;The Bitcoin whitepaper, written by &lt;strong&gt;Satoshi Nakamoto&lt;/strong&gt;, is often referred to as the &lt;em&gt;Bible of Cryptocurrency&lt;/em&gt;. Despite being only a few pages long, it proposed a groundbreaking solution to a centuries-old problem: how to create a trustless, decentralized system of value exchange.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The root problem with conventional currency is all the trust that’s required to make it work. The central bank must be trusted not to debase the currency, but the history of fiat currencies is full of breaches of that trust. Banks must be trusted to hold our money and transfer it electronically, but they lend it out in waves of credit bubbles with barely a fraction in reserve."&lt;br&gt;&lt;br&gt;
— &lt;em&gt;Satoshi Nakamoto, Bitcoin Whitepaper (2008)&lt;/em&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This completely changed my perspective. Bitcoin wasn’t just a digital coin—it was a &lt;strong&gt;revolution against traditional financial systems&lt;/strong&gt;.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Ethereum &amp;amp; The Rise of Smart Contracts
&lt;/h2&gt;

&lt;p&gt;Then came &lt;strong&gt;Ethereum&lt;/strong&gt;. While Bitcoin solved the problem of decentralized money, Ethereum took it a step further: it created a programmable blockchain, enabling Smart Contracts that could automate trust and transactions.  &lt;/p&gt;

&lt;p&gt;At first, I didn’t grasp the full implications. But when I encountered &lt;strong&gt;NFTs (Non-Fungible Tokens)&lt;/strong&gt;, everything clicked.  &lt;/p&gt;

&lt;p&gt;NFTs weren’t just about digital art. They represented ownership, proof of authenticity, and verifiable scarcity on the blockchain. I realized that anything that required proof—certificates, real estate, supply chain tracking—could one day exist as an NFT, secured by Smart Contracts.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Code is Law"—and for the first time, I saw &lt;strong&gt;a world where contracts, agreements, and transactions&lt;/strong&gt; could be &lt;strong&gt;trustless, transparent, and immutable.&lt;/strong&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s when I knew:  &lt;/p&gt;

&lt;p&gt;This was more than just an investment opportunity—it was a technological revolution.  &lt;/p&gt;

&lt;p&gt;The world was moving fast, and if I didn’t jump in now, I’d be left behind.  &lt;/p&gt;

&lt;p&gt;I didn’t just want to be a passive observer—I wanted to be a builder.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters to Me
&lt;/h2&gt;

&lt;p&gt;Technology is most powerful when it empowers people. With Smart Contracts, I see a future where:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Artists no longer need intermediaries to sell their work.
&lt;/li&gt;
&lt;li&gt;Supply chains are transparent, ensuring ethical sourcing.
&lt;/li&gt;
&lt;li&gt;Real estate transactions happen without unnecessary bureaucracy.
&lt;/li&gt;
&lt;li&gt;Financial systems are truly accessible to everyone—not just the privileged few.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I asked myself:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If I could contribute to making this future a reality—even in the smallest way—wouldn’t that be worth the switch?"  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer was obvious.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Join Me on This Journey
&lt;/h2&gt;

&lt;p&gt;This is just the beginning of my &lt;strong&gt;Web3 developer journey&lt;/strong&gt;. If you’re also learning &lt;strong&gt;Solidity, Smart Contracts, or blockchain development&lt;/strong&gt;, let’s connect!  &lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/benzdriver" rel="noopener noreferrer"&gt;https://github.com/benzdriver&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/ziyan-zhou/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/ziyan-zhou/&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Let’s build the future of blockchain together.  &lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>smartcontract</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
