Hashtag Web3 / Updated
Web3 Security Best Practices for Developers
A important guide for Web3 developers. Learn the essential security best practices for writing smart contracts, from the Checks-Effects-Interactions pattern.
In Web3, developers face high stakes. A flaw in a smart contract can lead to significant financial losses, potentially draining billions in value from user accounts. The immutable nature of the blockchain means there are no chances for correction. Security is essential for any project in this field.
This guide details critical security best practices that every smart contract developer should understand. It highlights common attack methods and outlines programming strategies to defend against them.
1. The Checks-Effects-Interactions Pattern
This design pattern is vital in Solidity to prevent a common vulnerability known as reentrancy.
- Reentrancy Vulnerability: A reentrancy attack happens when a malicious external contract makes a call back into your contract before the initial function call completes. This allows the attacker to exploit the withdrawal function, draining funds before the balance updates.
- Implementation Strategy: Organize your functions in this order:
- Checks: Validate conditions first (e.g.,
require(msg.sender == owner)). - Effects: Update your contract's state (e.g.,
balances[msg.sender] = 0). - Interactions: Finally, interact with any external contracts (e.g.,
(bool sent, ) = msg.sender.call{value: amount}("")).
By adjusting the state before transferring funds, you protect your contract from multiple withdrawals triggered by an external contract.
2. Prioritize call for External Calls
When sending Ether from a contract, always prefer {value: amount}("") instead of .transfer() or .send().
- Gas Limit Issue: The
transfer()andsend()methods provide a fixed gas stipend. While intended as a safeguard, this can cause failures due to changing gas costs in future network updates. A contract with a more complex fallback function might run out of gas, leading to transaction reverts. - Future-Proofing Solution:
{value: amount}("")sends all remaining gas, enhancing your contract's resilience against future changes. However, this reinforces the need for the Checks-Effects-Interactions pattern to mitigate reentrancy risks.
3. Safeguard Against Integer Overflows and Underflows
Prior to Solidity version 0.8.0, arithmetic operations did not revert upon overflow or underflow.
- Overflow and Underflow Risks: For example, if a
uint8(0-255 range) is at 255 and you add 1, it wraps to 0. An attacker could exploit this to modify balances or other critical values. - Mitigation Techniques:
- Use Solidity 0.8.0+: All modern contracts should specify
pragma solidity ^0.8.0;. This version automatically reverts on overflow or underflow. - Use SafeMath (Legacy): For older contracts, implement OpenZeppelin's SafeMath library for arithmetic operations.
4. Prevent Transaction-Ordering Dependence
Do not assume that the order of transactions in the mempool reflects their execution order in a block. Malicious actors can see your transaction and pay a higher gas fee to prioritize their own.
- Example of Front-running: If you position a reward for the first correct answer in a smart contract, a front-running bot can see the correct answer in the mempool and claim the prize for itself.
- Commit-Reveal Solution: Use a commit-reveal scheme where users first submit a hash of their answer. After the commit phase, they reveal the answer. This process prevents bots from accessing the solution beforehand.
5. Rely on Audited Libraries
Avoid creating your own versions of widely used standards like tokens.
- Implementation Risks: Developing your own ERC20 or ERC721 could introduce significant errors.
- Best Practice: Use established libraries such as OpenZeppelin Contracts. Their codes are rigorously audited and adhere to industry standards.
6. Establish a Thorough Test Suite and Obtain an Audit
- Testing Requirements: Aim for complete coverage in your test suite. Use tools like Foundry or Hardhat to write tests that account for every scenario, including edge cases and potential attack vectors.
- Audit Necessity: For contracts managing substantial value, securing a professional audit from a reputable firm is essential. An audit helps identify vulnerabilities that you might overlook.
Building applications in Web3 demands a cautious mindset. Assume that all external contracts could be hostile and that skilled attackers will seek to exploit any vulnerabilities. By implementing these security best practices, you can enhance the safety of your applications.

