<?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: ArefXV</title>
    <description>The latest articles on DEV Community by ArefXV (@xvishere).</description>
    <link>https://dev.to/xvishere</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%2F3252189%2F4064abbe-fbac-41be-9d5d-e3d006f301d0.png</url>
      <title>DEV Community: ArefXV</title>
      <link>https://dev.to/xvishere</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xvishere"/>
    <language>en</language>
    <item>
      <title>How ETH Is Received in Smart Contracts</title>
      <dc:creator>ArefXV</dc:creator>
      <pubDate>Sat, 14 Jun 2025 17:19:25 +0000</pubDate>
      <link>https://dev.to/xvishere/how-eth-is-received-in-smart-contracts-2khk</link>
      <guid>https://dev.to/xvishere/how-eth-is-received-in-smart-contracts-2khk</guid>
      <description>&lt;h1&gt;
  
  
  How ETH Is Received in Smart Contracts — &lt;code&gt;receive()&lt;/code&gt;, &lt;code&gt;fallback()&lt;/code&gt;, and Common Mistakes
&lt;/h1&gt;

&lt;p&gt;Smart contracts on Ethereum can receive ETH directly. But what actually happens when someone sends ETH to a contract? And why do some transactions fail silently while others go through?&lt;/p&gt;

&lt;p&gt;Let’s dive deep into how Ether is handled in Solidity, the differences between &lt;code&gt;receive()&lt;/code&gt; and &lt;code&gt;fallback()&lt;/code&gt;, and some key developer mistakes you should avoid.&lt;/p&gt;




&lt;h2&gt;
  
  
  ETH Transfers 101
&lt;/h2&gt;

&lt;p&gt;When you send ETH to a contract, one of two functions will be triggered based on the context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;receive() external payable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fallback() external payable&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are &lt;strong&gt;special functions&lt;/strong&gt; in Solidity that aren’t called directly in code. They get triggered under very specific circumstances.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Does &lt;code&gt;receive()&lt;/code&gt; Run?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;receive()&lt;/code&gt; function runs when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A contract receives &lt;strong&gt;ETH with empty calldata&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;receive()&lt;/code&gt; function is marked &lt;strong&gt;payable&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;receive() external payable {
    emit Received(msg.sender, msg.value);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If someone calls the contract with just ETH (e.g., using .transfer() or sending ETH directly via MetaMask), this function will run.&lt;/p&gt;

&lt;p&gt;Use case: Simple ETH deposits with no logic or function calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Does fallback() Run?
&lt;/h2&gt;

&lt;p&gt;The fallback() function is more general. It runs when:&lt;/p&gt;

&lt;p&gt;A function is called that doesn’t exist&lt;/p&gt;

&lt;p&gt;OR when calldata is not empty&lt;/p&gt;

&lt;p&gt;AND receive() is either not present or doesn’t match&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fallback() external payable {
    emit FallbackCalled(msg.sender, msg.value, msg.data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also define a non-payable fallback if you want to reject ETH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fallback() external {
    revert("ETH not accepted");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Developer Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Not defining receive() or fallback()
&lt;/h3&gt;

&lt;p&gt;If your contract is meant to accept ETH but has neither, any direct ETH transfer will revert.&lt;/p&gt;

&lt;p&gt;Fix: Add at least one payable function (receive() or fallback()).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Heavy Logic in Fallbacks
&lt;/h3&gt;

&lt;p&gt;Since fallback functions can get triggered unintentionally, avoid writing logic-heavy operations that might increase gas use or create vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Not Handling msg.data
&lt;/h3&gt;

&lt;p&gt;Sometimes, users (or attackers) send ETH with extra calldata. If you're only using receive(), those transactions will revert. Always consider a fallback for maximum safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example: Accepting ETH in a Treasury Contract
&lt;/h2&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.25;

contract Treasury {
    address public owner;

    event Received(address sender, uint256 amount);
    event FallbackCalled(address sender, uint256 amount, bytes data);

    constructor() {
        owner = msg.sender;
    }

    receive() external payable {
        emit Received(msg.sender, msg.value);
    }

    fallback() external payable {
        emit FallbackCalled(msg.sender, msg.value, msg.data);
    }

    function withdraw() external {
        require(msg.sender == owner, "Only owner");
        payable(owner).transfer(address(this).balance);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try sending ETH from your wallet or via Remix — both receive and fallback will log different events based on how the ETH was sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Vyper?
&lt;/h2&gt;

&lt;p&gt;Vyper handles this similarly but doesn’t use receive() or fallback() keywords directly. It uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@external
@payable
def __default__():
    # Code here runs on unknown function call or direct ETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is handled in &lt;strong&gt;default&lt;/strong&gt;, so you must manually manage what to accept and reject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;p&gt;Always define receive() if your contract is meant to receive ETH directly.&lt;/p&gt;

&lt;p&gt;Use a fallback() to catch unexpected calls or ETH sent with data.&lt;/p&gt;

&lt;p&gt;Don’t rely on these functions for business logic.&lt;/p&gt;

&lt;p&gt;Always emit logs for transparency (emit Received(...)) for off-chain monitoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;Many developers forget to test how their contracts behave with unexpected inputs or ETH sent manually. A missing receive() function can easily break dApps like fundraising contracts, treasuries, or NFT mints.&lt;/p&gt;

&lt;p&gt;When building production-grade contracts, always include proper ETH handling, and test them via .call, .transfer, or even raw transactions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want me to cover gas optimizations, reentrancy, or cross-chain bridging next? Stay tuned.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>solidity</category>
      <category>smartcontract</category>
      <category>evm</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Access Control in Solidity: Beyond onlyOwner</title>
      <dc:creator>ArefXV</dc:creator>
      <pubDate>Tue, 10 Jun 2025 06:38:03 +0000</pubDate>
      <link>https://dev.to/xvishere/access-control-in-solidity-beyond-onlyowner-2nlh</link>
      <guid>https://dev.to/xvishere/access-control-in-solidity-beyond-onlyowner-2nlh</guid>
      <description>&lt;p&gt;Access control is the backbone of secure smart contract design.&lt;/p&gt;

&lt;p&gt;Whether you’re building a DeFi protocol, NFT minting contract, or on-chain governance system — you need to control who can do what.&lt;/p&gt;

&lt;p&gt;But too often, developers default to a single onlyOwner check and call it a day. That might work for simple prototypes — but in production systems, it’s dangerous, inflexible, and often unsustainable.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll walk through the most important access control patterns in Solidity, their use cases, trade-offs, and how to do it right.&lt;/p&gt;

&lt;p&gt;** The Foundations: onlyOwner and Ownable**&lt;/p&gt;

&lt;p&gt;OpenZeppelin’s Ownable contract is the simplest form of access control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function transferOwnership(address newOwner) public onlyOwner;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You define an owner, and functions are restricted via the onlyOwner modifier.&lt;/p&gt;

&lt;p&gt;✅ Pros:&lt;br&gt;
    • Simple and lightweight&lt;br&gt;
    • Perfect for early-stage prototypes or single-admin tools&lt;/p&gt;

&lt;p&gt;❌ Cons:&lt;br&gt;
    • No flexibility for multiple roles&lt;br&gt;
    • Hard to decentralize&lt;br&gt;
    • Difficult to scale with teams, DAOs, or governance&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role-Based Access: OpenZeppelin’s AccessControl&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For more complex permissioning, OpenZeppelin provides a Role-Based Access Control (RBAC) system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

function mint() public onlyRole(MINTER_ROLE) { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can create any number of roles (e.g. PAUSER_ROLE, BURNER_ROLE) and assign/revoke them dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grantRole(MINTER_ROLE, user);
revokeRole(MINTER_ROLE, user);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Pros:&lt;br&gt;
    • Fine-grained control&lt;br&gt;
    • Roles can be assigned to different addresses&lt;br&gt;
    • Great for modular permissioning&lt;br&gt;
    • Integrates well with on-chain governance&lt;/p&gt;

&lt;p&gt;❌ Cons:&lt;br&gt;
    • Slightly more gas&lt;br&gt;
    • Requires thoughtful role management&lt;br&gt;
    • No built-in multi-sig support (but can integrate)&lt;/p&gt;

&lt;p&gt;Modular Role Design: Real-World Patterns&lt;/p&gt;

&lt;p&gt;Here are a few useful role patterns used in serious protocols:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pausable Pattern&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;function pause() external onlyRole(PAUSER_ROLE);
function unpause() external onlyRole(PAUSER_ROLE);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allows freezing protocol functions in emergencies (e.g., hacks, oracle failures).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operator/Admin Split&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ADMIN_ROLE: can assign/revoke other roles&lt;br&gt;&lt;br&gt;
OPERATOR_ROLE: can perform daily actions (e.g., rebalance, harvest, trigger events)&lt;/p&gt;

&lt;p&gt;Keeps critical keys cold and uses hot wallets only for limited functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DAO-Ready Roles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When building for DAOs, make all roles assignable via proposals, not by a single owner.&lt;/p&gt;

&lt;p&gt;Use:&lt;br&gt;
    • A TimelockController contract&lt;br&gt;
    • Role assignments gated by governance&lt;br&gt;
    • Non-upgradeable core + upgradeable modules&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade-Safe Access Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rule of thumb: When upgrading contracts via proxies, access control must live in the same storage layout.&lt;/p&gt;

&lt;p&gt;You can:&lt;br&gt;
    • Store roles in the proxy&lt;br&gt;
    • Avoid accidentally removing access in the next version&lt;br&gt;
    • Reserve storage slots for future roles (just in case)&lt;/p&gt;

&lt;p&gt;Always test upgrades using tools like OpenZeppelin’s Upgrades plugin to ensure storage layout isn’t corrupted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Mistakes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Hardcoding a single admin&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;require(msg.sender == 0x123...); // Don’t do this
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not flexible, not testable, and hard to migrate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Using onlyOwner in upgradeable contracts without OwnableUpgradeable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Solidity doesn’t automatically preserve Ownable state unless you inherit from the correct proxy-safe version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Forgetting to emit access change events&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Always emit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event RoleGranted(bytes32 role, address account, address sender);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without logs, tracking permissions is nearly impossible&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to Use Which&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Small contracts / MVP =&amp;gt; Ownable&lt;br&gt;
Multi-role systems =&amp;gt; AccessControl&lt;br&gt;
Governance / Timelock =&amp;gt; AccessControl + TimelockController&lt;br&gt;
Protocols with emergency handling =&amp;gt; Add Pausable role&lt;br&gt;
Teams with cold/hot wallets =&amp;gt; Admin/operator split&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>web3</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
