
Hardhat solidity is an important tool for developers, which offers a powerful test and distribution environment. Writing and distributing smart contracts, however, is not just about working – it’s about that makes them safe and effective.
In this chapter, we will dive into larger safety practices, purest strategies and gas adaptation techniques that each developer should know. Towards the end, you have a solid understanding how to protect private keys, retire smartly, writes gas-capable solidity and benefits from Openzeplin for safety.
Let’s get started!
Table of Contents
1. Protecting Private Keys in Hardhat
Why Private Key Security Matters
Your personal key is as a main password for the Blockchain wallet and smart contract. Hvis noen har tilgang til det, kan de tømme midlene dine, kidnappe kontraktene dine eller distribuere ondsinnet kode under ditt navn
Unfortunately, many developers are mistaken without revealing their personal key without realizing it.
General errors leading to private key exposure
❌ Hardcoding private keys hardhat.config.js
❌ Pushing private keys to GitHub by mistake
❌ Using public RPC endpoints without proper restrictions
Best Practices to Keep Private Keys Safe
1. Use Environment Variables (dotenv
) Instead of Hardcoding
Instead of putting your private key in your code, store it in an environment file:
- Install dotenv
npm install dotenv
- Create a
.env
file and add your private keyPRIVATE_KEY=your_private_key_here
- Update
hardhat.config.js
to read from.env
require("dotenv").config(); module.exports = { networks: { mainnet: { url: process.env.ALCHEMY_MAINNET_URL, accounts: [process.env.PRIVATE_KEY], }, }, };
🔹 Why? Your private key remains hidden from your codebase and never gets pushed to GitHub
2. Use a Hardware Wallet for Deployment
Instead of storing private keys in .env
, use a Ledger or Trezor hardware wallet for Added security.
3. Restrict RPC Access to Trusted IPs
If you’re using Infura or Alchemy, set up IP whitelisting to prevent unauthorized access to your Ethereum node.
2. Hardhat Deploy vs. Manual Deployment
Manual Deployment: The Traditional Approach
Most beginners deploy contracts manually by writing scripts. While this works, it’s:
❌ Prone to errors (e.g., forgetting constructor arguments)
❌ Not scalable (on every occasion you deploy, you need to replace the script).
❌ Difficult to manage for multiple networks
Here’s a simple example of manual deployment:
async function main() {
const MyContract = await ethers.getContractFactory("MyContract");
const deployedContract = await MyContract.deploy("Initial Value");
console.log("Contract deployed to:", deployedContract.address);
}
main();
Hardhat Deploy: A Smarter Alternative
Hardhat Deploy automates the process, making it repeatable, structured, and error-free.
✅ Standardized deployment scripts
✅ Supports multi-network deployments
✅ Built-in logging and verification
Example of a Hardhat Deploy script:
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
await deploy("MyContract", {
from: deployer,
args: ["Initial Value"],
log: true,
});
};
Why use Hardhat Deploy?
With Hardhat Deploy, you don’t have to manually update scripts or worry about errors. Just run the deployment command, and it handles everything.
3. Writing Gas-Efficient Smart Contracts
Why Does Gas Optimization Matter?
Ethereum gas tax can be expensive . especially under high network closure. Poorly optimized contracts can be used 30-50% more in gasoline required.
Common Gas-Heavy Mistakes
❌ Using memory
when calldata
is cheaper
❌ Writing to storage unnecessarily
❌ Using loops over large arrays
Best Practices for Gas Efficiency
1. Use calldata
Instead of memory
for Function Parameters
calldata
is cheaper because it doesn’t modify state.
function processArray(uint[] calldata numbers) external {
for (uint i = 0; i < numbers.length; i++) {
// Processing
}
}
2. Minimize Storage Writes
Writing to storage costs a lot of gas, so avoid unnecessary updates.
❌ Bad practice:
function updateValue(uint newValue) external {
value = newValue; // Writes to storage every time
}
✅ Better approach:
function updateValue(uint newValue) external {
if (value != newValue) {
value = newValue; // Writes only when necessary
}
}
3. Use Mappings Instead of Arrays for Lookups
Instead of looping through an array, use a mapping:
mapping(address => uint) balances;
Mappings allow instant lookups, unlike arrays which require iteration.
4. Using OpenZeppelin for Security
Why Use OpenZeppelin?
Writing smart contracts with scratches is risky. Openzeppelin provides for war tests, revised contracts :
✅ Tokens (ERC-20, ERC-721, ERC-1155)
✅ Access control (Ownable
, Roles
)
✅ Secure contract upgrades
Example: Secure ERC-20 Token with OpenZeppelin
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
📌 Why? OpenZeppelin removes security risks by providing pre-audited implementations.
5. FAQ – Common Hardhat Security Questions
Q1: What is the best way to safely store private keys?
A: Use environment variables (dotenv
), hardware wallets, or multi-signature wallets.
Q2: How do I reduce gas fees while distributing contracts?
A: Use Hardhat Deploy, optimize storage usage, and use calldata instead of memory.
Q3: Is OpenZeppelin necessary for every project?
A: If you’re working with tokens, roles, or upgradeable contracts, OpenZeppelin saves time and improves security.
6. Final Thoughts & Takeaways
What We Covered:
✅ How to protect private keys
✅ Why Hardhat Deploy is better than manual deployment
✅ Gas-saving techniques for Solidity
✅ Why OpenZeppelin is a must for security
Final Advice
There is security and efficiency non–negotiable in smart contract development. By following these best practices, you will create fast, safe and more cost -effective contract .