<?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: Keijzer Rozenberg</title>
    <description>The latest articles on DEV Community by Keijzer Rozenberg (@keijzer_rozenberg_fa2c47c).</description>
    <link>https://dev.to/keijzer_rozenberg_fa2c47c</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%2F3559093%2F0bf2796c-32fd-4808-94b0-8ffcb588e0c5.png</url>
      <title>DEV Community: Keijzer Rozenberg</title>
      <link>https://dev.to/keijzer_rozenberg_fa2c47c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/keijzer_rozenberg_fa2c47c"/>
    <language>en</language>
    <item>
      <title>I Just Built &amp; Fuzz-Tested a Simple Bank Smart Contract Using Hardhat 3 + Viem + Solidity Tests</title>
      <dc:creator>Keijzer Rozenberg</dc:creator>
      <pubDate>Thu, 30 Oct 2025 09:08:24 +0000</pubDate>
      <link>https://dev.to/keijzer_rozenberg_fa2c47c/i-just-built-fuzz-tested-a-simple-bank-smart-contract-using-hardhat-3-viem-solidity-tests-5ad5</link>
      <guid>https://dev.to/keijzer_rozenberg_fa2c47c/i-just-built-fuzz-tested-a-simple-bank-smart-contract-using-hardhat-3-viem-solidity-tests-5ad5</guid>
      <description>&lt;p&gt;Today I leveled up my Web3 dev workflow.&lt;br&gt;
Instead of only coding in Remix (which is great for learning), I set up a full professional flow:&lt;/p&gt;

&lt;p&gt;Hardhat 3&lt;/p&gt;

&lt;p&gt;Viem for deployment &amp;amp; contract interaction&lt;/p&gt;

&lt;p&gt;Solidity-based tests (yes, like Foundry!)&lt;/p&gt;

&lt;p&gt;Fuzz testing&lt;/p&gt;

&lt;p&gt;Reentrancy-safe withdraw function&lt;/p&gt;

&lt;p&gt;This was the first time I try testFuzz_...&lt;/p&gt;

&lt;p&gt;I’ve always deployed contracts from Remix before. But today I forced myself to do it the “real engineer” way: automated tests, deterministic deployment scripts, event checks, and fuzzing.&lt;/p&gt;

&lt;p&gt;Here’s the smart contract I built:&lt;br&gt;
A simple on-chain bank that supports:&lt;/p&gt;

&lt;p&gt;✔ Deposit&lt;br&gt;
✔ Withdraw&lt;br&gt;
✔ Account balance tracking&lt;br&gt;
✔ Transaction history mapping&lt;br&gt;
✔ Events&lt;br&gt;
✔ Reentrancy protection&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contract Code&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;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract SimpleBank is ReentrancyGuard {

    struct TxRecord {
        address user;
        bytes32 action; // [ DEPOSIT | WITHDRAW ]
        uint256 amount;
    }

    // balances per user
    mapping(address =&amp;gt; uint256) private balances;

    // transaction counter
    uint256 public transactionCount;

    // list of transaction per user
    mapping(uint256 =&amp;gt; TxRecord) public transactions;

    // events
    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);

    function deposit() external payable {
        require(msg.value &amp;gt; 0, "no zero deposit");

        // update internal balances
        balances[msg.sender] += msg.value;

        // log transaction
        transactionCount += 1;
        transactions[transactionCount] = TxRecord({
            user:msg.sender,
            action:bytes32("DEPOSIT"),
            amount:msg.value
        });

        // event emit
        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 amount) external nonReentrant {
        require(balances[msg.sender] &amp;gt;= amount, "not enough balance");

        // update internal balances
        balances[msg.sender] -= amount;

        // interaction last (safer)
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "withdraw failed");

        // log transaction
        transactionCount += 1;
        transactions[transactionCount] = TxRecord({
            user:msg.sender,
            action:bytes32("WITHDRAW"),
            amount:amount
        });

        // event emit
        emit Withdraw(msg.sender, amount);
    }

    function balanceOf(address user) external view returns (uint256) {
        return balances[user];
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solidity Test&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;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "forge-std/Test.sol";
import "../contracts/SimpleBank.sol";

contract SimpleBankTest is Test {
    SimpleBank private bank;
    address private user = address(0xBEEF);

    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);

    function setUp() public {
        bank = new SimpleBank();
        vm.deal(user, 10 ether);
    }

    function test_Deposit_IncreasesBalance_And_Records() public {
        vm.prank(user);
        bank.deposit{value: 1000}();

        assertEq(bank.balanceOf(user), 1000);

        uint256 txCount = bank.transactionCount();
        assertEq(txCount, 1);

        (address u, bytes32 action, uint256 amount) = bank.transactions(txCount);
        assertEq(u, user);
        assertEq(action, bytes32("DEPOSIT"));
        assertEq(amount, 1000);
    }

    function test_Withdraw_DecreasesBalance_And_Records() public {
        vm.prank(user);
        bank.deposit{value: 1000}();

        vm.prank(user);
        bank.withdraw(10);

        assertEq(bank.balanceOf(user), 990);

        uint256 txCount = bank.transactionCount();
        assertEq(txCount, 2);

        (, bytes32 action, uint256 amount) = bank.transactions(txCount);
        assertEq(action, bytes32("WITHDRAW"));
        assertEq(amount, 10);
    }

    function test_Revert_When_Withdraw_More_Than_Balance() public {
        vm.prank(user);
        vm.expectRevert(bytes("not enough balance"));
        bank.withdraw(1);
    }

    // Fuzz example
    function testFuzz_DepositWithdraw(uint128 amt) public {
        vm.assume(amt &amp;gt; 0 &amp;amp;&amp;amp; amt &amp;lt;= 10 ether); // match vm.deal(user, 10 ether)
        vm.prank(user);
        bank.deposit{value: amt}();
        assertEq(bank.balanceOf(user), amt);

        vm.prank(user);
        bank.withdraw(amt);
        assertEq(bank.balanceOf(user), 0);
    }

    function test_Events_Emitted() public {
        vm.prank(user);
        vm.expectEmit(true, true, false, true, address(bank));
        emit Deposit(user, 1000);
        bank.deposit{value: 1000}();

        vm.prank(user);
        vm.expectEmit(true, true, false, true, address(bank));
        emit Withdraw(user, 10);
        bank.withdraw(10);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NodeJs Test&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;import { artifacts, network } from 'hardhat';
import assert from "node:assert/strict";
import test, { before } from 'node:test';

function bytes32ToString(bytes32Value: string): string {
  let hex = bytes32Value.startsWith("0x") ? bytes32Value.slice(2) : bytes32Value;
  while (hex.endsWith("00")) hex = hex.slice(0, -2);
  let out = "";
  for (let i = 0; i &amp;lt; hex.length; i += 2) out += String.fromCharCode(parseInt(hex.slice(i, i + 2), 16));
  return out;
}

let publicClient: any;
let walletClient: any;
let contractAddress: `0x${string}`;
let abi: any;

before(async () =&amp;gt; {
    const { viem } = await network.connect({
        network: "hardhatOp",
        chainType: "op",
    });

    publicClient = await viem.getPublicClient();
    [walletClient] = await viem.getWalletClients();
    const artifact = await artifacts.readArtifact("SimpleBank");
    abi = artifact.abi;

    const deployHash = await walletClient.deployContract({
        abi,
        bytecode: artifact.bytecode as `0x${string}`
    })

    const receipt = await publicClient.waitForTransactionReceipt({ hash: deployHash });
    contractAddress = receipt.contractAddress as `0x${string}`;
})


const getBalance = async (addr: `0x${string}`) =&amp;gt; {
    return (await publicClient.readContract({
        address: contractAddress,
        abi,
        functionName: 'balanceOf',
        args: [addr]
    })) as bigint;
};

const getLastTx = async () =&amp;gt; {
    const raw = await publicClient.readContract({
        address: contractAddress,
        abi,
        functionName: 'transactionCount',
    });
    const txCount = BigInt(raw as any);
    const tx = await publicClient.readContract({
        address: contractAddress,
        abi,
        functionName: 'transactions',
        args: [txCount]
    });
    return {txCount, tx};
}

test("deposit Increase balance and records DEPOSIT", async () =&amp;gt; {
    const addr = walletClient.account.address as `0x${string}`;
    const before = await getBalance(addr);
    assert.equal(before, 0n);

    const txHash = await walletClient.writeContract({
        address: contractAddress,
        abi,
        functionName: 'deposit',
        args: [],
        value: 1000n
    });
    await publicClient.waitForTransactionReceipt({hash: txHash});
    const after = await getBalance(addr);
    assert.equal(after, 1000n)

    const {txCount, tx} = await getLastTx();
    assert.equal(txCount, 1n);
    assert.equal((tx[0] as string).toLowerCase(), addr.toLowerCase())
    assert.equal(bytes32ToString(tx[1] as string), "DEPOSIT"); 
    assert.equal(tx[2], 1000n)
})

test("withdraw decrease balance and records WITHDRAW", async () =&amp;gt; {
    const addr = walletClient.account.address as `0x${string}`
    const before = await getBalance(addr)
    assert.equal(before, 1000n)
    const  txhash = await walletClient.writeContract({
        address: contractAddress,
        abi,
        functionName: 'withdraw',
        args: [223n]
    })
    await publicClient.waitForTransactionReceipt({hash: txhash})
    const after = await getBalance(addr)
    assert.equal(after, 777n)
})

test("withdraw more than balance should revert", async () =&amp;gt; {
    try {
        const txHash = await walletClient.writeContract({
            address: contractAddress,
            abi,
            functionName: 'withdraw',
            args: [1000000n]
        })
        await publicClient.waitForTransactionReceipt({hash : txHash})
        assert.fail('expected revert but tx succeeded')
    } catch (e: any) {
        const msg = String(e?.message || e);
        assert.ok(msg.includes('not enough balance'), "revert reason mismatch: " + msg);
    }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running Solidity tests&lt;/p&gt;

&lt;p&gt;✔ test_Deposit_IncreasesBalance_And_Records()&lt;br&gt;
✔ test_Withdraw_DecreasesBalance_And_Records()&lt;br&gt;
✔ test_Revert_When_Withdraw_More_Than_Balance()&lt;br&gt;
✔ test_Events_Emitted()&lt;br&gt;
✔ testFuzz_DepositWithdraw(uint128) (runs: 256)&lt;/p&gt;

&lt;p&gt;All good ✅&lt;/p&gt;

&lt;p&gt;I used to think smart contract testing = just a few asserts in JS.&lt;/p&gt;

&lt;p&gt;But Solidity-native testing + fuzzing feels like superpowers.&lt;/p&gt;

&lt;p&gt;Lessons today:&lt;/p&gt;

&lt;p&gt;💡 Remix is great to start, but real workflows = reproducible scripts + tests&lt;br&gt;
💡 Fuzz testing catches edge cases you don’t think about&lt;br&gt;
💡 Reentrancy guard is non-negotiable for contracts holding ETH&lt;br&gt;
💡 Hardhat 3 + Viem + forge-std = chef’s kiss for Web3 devs 🍷✨&lt;/p&gt;

&lt;p&gt;Next step: connecting a frontend (Wagmi + Next.js) + deploying to Sepolia.&lt;/p&gt;

&lt;p&gt;If you're learning Web3 and still coding only inside Remix, try this setup next — it feels like stepping into Level 2.&lt;/p&gt;

&lt;p&gt;If you want the full repo + guide, drop a comment!&lt;br&gt;
Happy building and secure your withdraw calls 🔐🚀&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>web3</category>
      <category>hardhat</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>My First Smart Contract Project on Sepolia</title>
      <dc:creator>Keijzer Rozenberg</dc:creator>
      <pubDate>Sat, 11 Oct 2025 19:46:54 +0000</pubDate>
      <link>https://dev.to/keijzer_rozenberg_fa2c47c/my-first-smart-contract-project-on-sepolia-4n28</link>
      <guid>https://dev.to/keijzer_rozenberg_fa2c47c/my-first-smart-contract-project-on-sepolia-4n28</guid>
      <description>&lt;p&gt;I've just built a simple Web3 project on the Sepolia testnet. it’s a faucet that works like a lottery game using a 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.amazonaws.com%2Fuploads%2Farticles%2F31wgb72nxt93v65sk15n.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.amazonaws.com%2Fuploads%2Farticles%2F31wgb72nxt93v65sk15n.png" alt=" " width="800" height="883"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 You can try it here:&lt;br&gt;
&lt;a href="https://sepolia-lottery-dapp-2.vercel.app/" rel="noopener noreferrer"&gt;my web&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s my first build in Web3, and I’m still learning Solidity and smart contract development — so any feedback, suggestions, or criticism are super welcome! 🙏&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>web3</category>
      <category>solidity</category>
      <category>programming</category>
    </item>
    <item>
      <title>hello everyone, could you guys give me a roadmap to become an web3 or blockchain engineer? because i see an opportunity in the future about this field. btw im newbie here.</title>
      <dc:creator>Keijzer Rozenberg</dc:creator>
      <pubDate>Sat, 11 Oct 2025 11:45:31 +0000</pubDate>
      <link>https://dev.to/keijzer_rozenberg_fa2c47c/hello-everyone-could-you-guys-give-me-a-roadmap-to-become-an-web3-or-blockchain-engineer-because-4icp</link>
      <guid>https://dev.to/keijzer_rozenberg_fa2c47c/hello-everyone-could-you-guys-give-me-a-roadmap-to-become-an-web3-or-blockchain-engineer-because-4icp</guid>
      <description></description>
      <category>beginners</category>
      <category>blockchain</category>
      <category>career</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
