projectrules.ai

Hardhat Best Practices and Coding Standards

hardhatethereumsmart-contractssoliditydapps

Description

This rule outlines best practices for Hardhat development, covering code organization, security, testing, and performance. It aims to provide a comprehensive guide for developers working with the Hardhat Ethereum development environment.

Globs

**/*.{js,ts,sol,json}
---
description: This rule outlines best practices for Hardhat development, covering code organization, security, testing, and performance. It aims to provide a comprehensive guide for developers working with the Hardhat Ethereum development environment.
globs: **/*.{js,ts,sol,json}
---

# Hardhat Best Practices and Coding Standards

This document provides a comprehensive guide to best practices for developing smart contracts and decentralized applications (dApps) using Hardhat. It covers code organization, security, testing, performance, and other essential aspects of Hardhat development.

## 1. Code Organization and Structure

### 1.1. Directory Structure Best Practices


hardhat-project/
├── contracts/          # Solidity smart contracts
│   ├── MyContract.sol
│   └── ...
├── scripts/            # Deployment and interaction scripts
│   ├── deploy.js
│   ├── interact.js
│   └── ...
├── test/               # Unit and integration tests
│   ├── MyContract.test.js
│   └── ...
├── hardhat.config.js   # Hardhat configuration file
├── package.json        # Node.js package file
├── README.md           # Project documentation
└── .gitignore          # Git ignore file


-   **contracts/:**  Store all Solidity smart contracts in this directory.  Consider subdirectories for different modules or features.
-   **scripts/:**  Keep deployment, interaction, and other utility scripts in this directory.  Organize scripts by function or deployment environment.
-   **test/:**  Place all unit and integration tests in this directory.  Mirror the `contracts/` directory structure for test files.
-   **hardhat.config.js:**  Configure Hardhat settings in this file, including compiler versions, network configurations, and task definitions. Consider using TypeScript.
-   **package.json:**  Manage project dependencies and scripts using npm or yarn.
-   **README.md:**  Provide clear and concise documentation for your project.
-   **.gitignore:** Exclude unnecessary files from source control (e.g., build artifacts, node_modules).

### 1.2. File Naming Conventions

-   **Solidity Files:** Use PascalCase with the `.sol` extension for Solidity contract files (e.g., `MyContract.sol`).
-   **JavaScript/TypeScript Files:** Use camelCase with the `.js` or `.ts` extension for JavaScript/TypeScript files (e.g., `deploy.js`, `myContract.test.ts`).
-   **Test Files:**  Use the `.test.js` or `.test.ts` suffix to identify test files (e.g., `MyContract.test.js`).

### 1.3. Module Organization

-   **Separate Concerns:**  Divide your contracts into logical modules or components based on their functionality.
-   **Interfaces:**  Use interfaces to define the public API of your contracts.
-   **Libraries:**  Create reusable libraries for common functionalities to avoid code duplication.
-   **Abstract Contracts:**  Use abstract contracts to define common logic and state variables for related contracts.

### 1.4. Component Architecture

-   **Modular Design:** Design contracts as independent, reusable components.
-   **Composition:** Combine smaller components to create more complex systems.
-   **Minimal Dependencies:** Reduce dependencies between components to improve maintainability and testability.

### 1.5. Code Splitting Strategies

-   **Separate Logic and Data:**  Keep contract logic separate from data storage to improve readability and maintainability.
-   **External Libraries:**  Utilize external libraries (e.g., OpenZeppelin) for common functionalities to reduce code size and improve security.
-   **Proxy Patterns:**  Consider using proxy patterns to enable upgradeability and code splitting.

## 2. Common Patterns and Anti-patterns

### 2.1. Design Patterns Specific to Hardhat/Solidity

-   **Ownable:**  Control access to privileged functions using the Ownable pattern (from OpenZeppelin).
-   **Pausable:**  Implement a Pausable contract to temporarily halt contract functionality in case of emergencies.
-   **Pull over Push:**  Favor pull-based payment mechanisms over push-based mechanisms to mitigate reentrancy attacks.
-   **Proxy patterns (e.g., UUPS, Transparent Proxy):** For contract upgrades.

### 2.2. Recommended Approaches for Common Tasks

-   **Deployment:**  Use Hardhat's deployment scripts and network configurations to automate contract deployment.
-   **Testing:**  Employ Hardhat's testing framework and Chai matchers for comprehensive unit and integration testing.
-   **Interaction:**  Utilize Hardhat's console and Ethers.js for interacting with deployed contracts.
-   **Verification:**  Verify contract source code on Etherscan to enhance transparency and trust.

### 2.3. Anti-patterns and Code Smells to Avoid

-   **Unchecked Arithmetic:**  Use SafeMath or Solidity 0.8+ to prevent integer overflow/underflow.
-   **Reentrancy:**  Protect against reentrancy attacks by using the Checks-Effects-Interactions pattern or reentrancy guards (from OpenZeppelin).
-   **Denial of Service (DoS):**  Avoid patterns that can lead to DoS attacks, such as unbounded loops or expensive operations.
-   **Timestamp Dependence:**  Avoid relying on block timestamps for critical logic, as they can be manipulated by miners.
-   **Insufficient Gas Limits:** Ensure functions have sufficient gas limits, especially for complex operations.
-   **Hardcoding Addresses/Values:** Use configuration files or environment variables for addresses and other configurable values.

### 2.4. State Management Best Practices

-   **Minimize State Variables:**  Reduce the number of state variables to minimize storage costs and improve performance.
-   **Use Appropriate Data Types:** Choose the most efficient data types for state variables (e.g., `uint256` instead of `uint`).
-   **Immutability:** Use `immutable` variables when the value is assigned at construction and never changed after.
-   **Constants:** Use `constant` variables for gas optimization when the value is known at compile time and never changed after.
-   **Events:**  Emit events to track state changes and enable off-chain monitoring.

### 2.5. Error Handling Patterns

-   **Require Statements:**  Use `require` statements to validate inputs and enforce preconditions.
-   **Revert Statements:**  Use `revert` statements to handle exceptional conditions and return informative error messages.
-   **Custom Errors:** Use custom errors (Solidity 0.8.4+) for gas optimization and detailed error reporting.
-   **Try/Catch (Solidity 0.8.16+):** Implement Try/Catch blocks when calling external contracts.

## 3. Performance Considerations

### 3.1. Optimization Techniques

-   **Gas Optimization:**  Minimize gas consumption to reduce transaction costs.
-   **Data Packing:**  Pack multiple small variables into a single storage slot to reduce storage costs.
-   **Short Circuiting:**  Use short-circuiting evaluation in logical expressions to avoid unnecessary computations.
-   **Assembly Optimization:**  Use inline assembly for performance-critical sections of code.
-   **Caching:** Cache values that are frequently accessed.

### 3.2. Memory Management

-   **Minimize Memory Usage:**  Reduce memory usage to avoid out-of-gas errors.
-   **Storage vs. Memory:**  Use storage for persistent data and memory for temporary data.
-   **Delete Unused Variables:** Delete variables in memory after use to free up space.

### 3.3. Rendering Optimization (If applicable - for frontend DApps)

-   **Lazy Loading:**  Load components and data only when they are needed.
-   **Virtualization:**  Use virtualization techniques to render large lists efficiently.
-   **Memoization:**  Memoize computationally expensive functions to avoid redundant calculations.

### 3.4. Bundle Size Optimization (If applicable - for frontend DApps)

-   **Code Splitting:**  Split the application code into smaller bundles to improve initial load time.
-   **Tree Shaking:**  Remove unused code from the bundle using tree shaking.
-   **Minification:**  Minify JavaScript and CSS files to reduce bundle size.

### 3.5. Lazy Loading Strategies (If applicable - for frontend DApps)

-   **Dynamic Imports:**  Use dynamic imports to load modules on demand.
-   **Intersection Observer:**  Use the Intersection Observer API to load resources when they become visible.

## 4. Security Best Practices

### 4.1. Common Vulnerabilities and How to Prevent Them

-   **Reentrancy:** Use Checks-Effects-Interactions pattern. Employ reentrancy guards (@openzeppelin/contracts/security/ReentrancyGuard.sol).
-   **Integer Overflow/Underflow:** Use SafeMath or Solidity 0.8+.
-   **Denial of Service (DoS):**  Limit gas costs, avoid unbounded loops, and implement rate limiting.
-   **Timestamp Dependence:**  Avoid relying on block timestamps for critical logic.
-   **Front Running:**  Design contracts to be resilient to front-running attacks.
-   **Signature Replay:** Prevent signature replay attacks by using nonces or other unique identifiers.
-   **Cross-Site Scripting (XSS):**  Sanitize user inputs in frontend DApps to prevent XSS attacks.
-   **SQL Injection:**  Use parameterized queries to prevent SQL injection attacks in backend systems.
-   **Improper Access Control:**  Enforce strict access control policies to protect sensitive data and functions.

### 4.2. Input Validation

-   **Sanitize Inputs:**  Sanitize and validate all user inputs to prevent malicious data from entering the system.
-   **Check Data Types:**  Verify that inputs are of the expected data types.
-   **Limit Input Length:**  Restrict the length of input strings to prevent buffer overflows.
-   **Regular Expressions:**  Use regular expressions to validate complex input patterns.

### 4.3. Authentication and Authorization Patterns

-   **Role-Based Access Control (RBAC):**  Implement RBAC to manage user permissions.
-   **Attribute-Based Access Control (ABAC):**  Use ABAC for more fine-grained access control.
-   **Multi-Factor Authentication (MFA):**  Implement MFA to enhance security.
-   **OAuth:**  Use OAuth for secure delegation of access to third-party applications.

### 4.4. Data Protection Strategies

-   **Encryption:**  Encrypt sensitive data at rest and in transit.
-   **Hashing:**  Use hashing to store passwords securely.
-   **Salting:**  Salt passwords to prevent rainbow table attacks.
-   **Key Management:**  Implement secure key management practices.

### 4.5. Secure API Communication (If applicable - for backend systems)

-   **HTTPS:**  Use HTTPS for all API communication.
-   **API Keys:**  Use API keys to authenticate API requests.
-   **Rate Limiting:**  Implement rate limiting to prevent abuse.
-   **Input Validation:** Validate and sanitize all API inputs.

## 5. Testing Approaches

### 5.1. Unit Testing Strategies

-   **Test Driven Development (TDD):**  Write tests before writing code.
-   **Black Box Testing:**  Test the functionality of the contract without knowledge of the internal implementation.
-   **White Box Testing:**  Test the internal implementation of the contract, including branches and loops.
-   **Coverage Analysis:**  Use coverage analysis tools to ensure that all code is covered by tests.

### 5.2. Integration Testing

-   **Test Contract Interactions:**  Test the interactions between multiple contracts.
-   **Test External Dependencies:**  Test the integration with external dependencies, such as oracles or other smart contracts.

### 5.3. End-to-End Testing (If applicable - for frontend DApps)

-   **Simulate User Flows:**  Simulate real-world user flows to test the entire application stack.
-   **Automated Testing:**  Use automated testing tools to run end-to-end tests regularly.

### 5.4. Test Organization

-   **Arrange-Act-Assert:**  Organize tests using the Arrange-Act-Assert pattern.
-   **Descriptive Test Names:**  Use descriptive names for tests to clearly indicate their purpose.
-   **Test Suites:**  Group related tests into test suites.

### 5.5. Mocking and Stubbing

-   **Mock External Dependencies:**  Use mocking to isolate contracts from external dependencies during testing.
-   **Stub Function Calls:**  Use stubbing to replace function calls with predefined values or behaviors.

## 6. Common Pitfalls and Gotchas

### 6.1. Frequent Mistakes Developers Make

-   **Incorrectly handling gas costs**
-   **Failing to validate inputs**
-   **Ignoring security best practices**
-   **Not writing enough tests**
-   **Misunderstanding Ethereum's execution model**
-   **Not using the latest compiler version**

### 6.2. Edge Cases to Be Aware Of

-   **Integer overflow/underflow**
-   **Reentrancy attacks**
-   **Denial-of-service attacks**
-   **Front-running attacks**
-   **Signature replay attacks**

### 6.3. Version-Specific Issues

-   **Solidity compiler bugs:** Be aware of known bugs in the Solidity compiler and use appropriate workarounds.
-   **Hardhat version compatibility:** Ensure that Hardhat and its plugins are compatible with each other.

### 6.4. Compatibility Concerns

-   **EVM version compatibility:**  Consider the target EVM version when developing contracts.
-   **Web3.js/Ethers.js compatibility:** Ensure compatibility with the chosen JavaScript library for interacting with the blockchain.

### 6.5. Debugging Strategies

-   **Console.log:**  Use `console.log` statements to debug code.
-   **Hardhat console:** Use the Hardhat console for interactive debugging.
-   **Truffle debugger:**  Use the Truffle debugger for more advanced debugging.
-   **Remix IDE:** Use Remix IDE for online debugging.
-   **Solidity stack traces:**  Use Solidity stack traces to identify the source of errors.

## 7. Tooling and Environment

### 7.1. Recommended Development Tools

-   **Hardhat:**  Ethereum development environment.
-   **Visual Studio Code:**  Code editor with Solidity support.
-   **Remix IDE:**  Online Solidity IDE.
-   **Etherscan:**  Blockchain explorer.
-   **Ganache:**  Local Ethereum blockchain.
-   **OpenZeppelin:** Secure smart contract libraries.

### 7.2. Build Configuration

-   **Hardhat configuration file:**  Configure Hardhat settings in the `hardhat.config.js` file.
-   **Compiler version:**  Specify the Solidity compiler version in the Hardhat configuration file.
-   **Optimization settings:** Configure optimization settings in the Hardhat configuration file.

### 7.3. Linting and Formatting

-   **Solhint:**  Solidity linter.
-   **Prettier:**  Code formatter.
-   **ESLint:**  JavaScript/TypeScript linter.

### 7.4. Deployment Best Practices

-   **Automated deployments:**  Use Hardhat's deployment scripts to automate contract deployment.
-   **Network configurations:**  Configure network settings in the Hardhat configuration file.
-   **Gas price estimation:** Estimate gas prices before deploying contracts.
-   **Contract verification:**  Verify contract source code on Etherscan after deployment.

### 7.5. CI/CD Integration

-   **Continuous integration:**  Integrate Hardhat with CI/CD systems (e.g., GitHub Actions, Jenkins) to automate testing and deployment.