This cheat sheet covers the most frequently used patterns and components in Solidity clever agreement improvement. You can replica, customise, and reuse those snippets to streamline your initiatives.
Table of Contents
Cheat Sheet : Smart Contract Coding
1. Setting up the Basics
Pragma and SPDX License
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
Contract Structure
contract MyContract {
// State variables
address public owner;
// Events
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}
// Constructor
constructor() {
owner = msg.sender;
}
}
2. Common Modifiers
Only Owner
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}
Prevent Reentrancy
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
bool private locked;
3. Standard Events
Ownership Transferred
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
Funds Received
event FundsReceived(address indexed sender, uint256 amount);
4. Common Functions
Transfer Ownership
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "New owner is the zero address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
Withdraw Ether
function withdrawEther() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
Receive Ether
receive() external payable {
emit FundsReceived(msg.sender, msg.value);
}
5. Common Smart Contract Patterns
Mapping for Balances
mapping(address => uint256) public balances;
function deposit() public payable {
require(msg.value > 0, "Must send Ether");
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
Token Transfers (ERC20 Simplified)
mapping(address => uint256) public tokenBalance;
function transfer(address recipient, uint256 amount) public {
require(tokenBalance[msg.sender] >= amount, "Insufficient balance");
tokenBalance[msg.sender] -= amount;
tokenBalance[recipient] += amount;
}
6. ERC Standards
ERC20 Functions
function totalSupply() public view returns (uint256);
function balanceOf(address account) public view returns (uint256);
function transfer(address recipient, uint256 amount) public returns (bool);
function allowance(address owner, address spender) public view returns (uint256);
function approve(address spender, uint256 amount) public returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool);
ERC721 Functions (NFTs)
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
7. Gas Optimization Tips
- Use
uint256
instead of smaller integers to avoid implicit type conversion costs. - Pack tightly used
struct
andstorage
variables. - Avoid dynamic arrays or unbounded loops where possible.
- Use libraries like
SafeMath
or Solidity 0.8+ (built-in overflow checks). - Emit events for critical state changes to reduce on-chain data retrieval costs.
8. Security Practices
Avoid Reentrancy
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
Use require
for Input Validation
require(msg.sender != address(0), "Invalid address");
require(amount > 0, "Amount must be greater than 0");
Limit Gas Usage in Loops
for (uint256 i = 0; i < users.length && i < MAX_USERS; i++) {
// Perform operations
}
Secure Randomness (For Simple Use Cases)
function random() internal view returns (uint256) {
return uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty, msg.sender)));
}
9. Testing Frameworks
Hardhat Example Test
const { expect } = require("chai");
describe("MyContract", function () {
it("Should set the right owner", async function () {
const [owner] = await ethers.getSigners();
const MyContract = await ethers.getContractFactory("MyContract");
const contract = await MyContract.deploy();
expect(await contract.owner()).to.equal(owner.address);
});
});
10. Deployment Script Example
Hardhat Deployment Script
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with the account:", deployer.address);
const MyContract = await ethers.getContractFactory("MyContract");
const contract = await MyContract.deploy();
console.log("Contract deployed to:", contract.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Cheat Sheet: Integrating Smart Contracts with Frontend
1. Project Setup
Install Required Libraries
Use npm or yarn to install the necessary libraries.
npm install ethers web3 react-moralis dotenv
Setup Environment Variables
Create a .env
file for sensitive data like private keys, API keys, and contract addresses:
REACT_APP_CONTRACT_ADDRESS=<YOUR_SMART_CONTRACT_ADDRESS>
REACT_APP_PROVIDER_URL=<YOUR_BLOCKCHAIN_PROVIDER_URL>
2. Import Dependencies
In your React frontend, import the necessary libraries.
import { ethers } from "ethers";
import Web3 from "web3";
import contractABI from "./contractABI.json"; // Add your ABI JSON file
3. Connect to Blockchain
Connect Wallet (e.g., MetaMask)
const connectWallet = async () => {
if (window.ethereum) {
try {
const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
console.log("Connected account:", accounts[0]);
return accounts[0];
} catch (error) {
console.error("Error connecting wallet:", error);
}
} else {
alert("MetaMask is not installed!");
}
};
4. Initialize Smart Contract
Set Up a Provider and Contract Instance
const getContract = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contractAddress = process.env.REACT_APP_CONTRACT_ADDRESS;
const contract = new ethers.Contract(contractAddress, contractABI, signer);
return contract;
};
5. Interact with the Smart Contract
Read Data from the Smart Contract
const fetchData = async () => {
try {
const contract = await getContract();
const data = await contract.somePublicViewFunction();
console.log("Contract Data:", data);
return data;
} catch (error) {
console.error("Error fetching data:", error);
}
};
Write Data to the Smart Contract
const sendTransaction = async (param1, param2) => {
try {
const contract = await getContract();
const transaction = await contract.somePublicWriteFunction(param1, param2);
console.log("Transaction Hash:", transaction.hash);
await transaction.wait(); // Wait for confirmation
console.log("Transaction confirmed!");
} catch (error) {
console.error("Error sending transaction:", error);
}
};
6. Handle Events
Listen for Smart Contract Events
const listenToEvents = async () => {
try {
const contract = await getContract();
contract.on("SomeEventName", (param1, param2, event) => {
console.log("Event Data:", { param1, param2, event });
});
} catch (error) {
console.error("Error listening to events:", error);
}
};
7. Detect Network Changes
Handle Network Switch
const handleNetworkChange = async () => {
if (window.ethereum) {
window.ethereum.on("chainChanged", (chainId) => {
console.log("Network changed to:", parseInt(chainId, 16));
window.location.reload(); // Reload app to switch networks
});
}
};
8. Wallet Disconnect
Handle Wallet Disconnect
const handleDisconnect = () => {
if (window.ethereum) {
window.ethereum.on("disconnect", () => {
console.log("Wallet disconnected!");
});
}
};
9. Frontend Integration Example
React Example with Hooks
import React, { useState, useEffect } from "react";
import { ethers } from "ethers";
import contractABI from "./contractABI.json";
const App = () => {
const [account, setAccount] = useState(null);
const [data, setData] = useState("");
const connectWallet = async () => {
if (window.ethereum) {
const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
setAccount(accounts[0]);
} else {
alert("Please install MetaMask!");
}
};
const fetchData = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(process.env.REACT_APP_CONTRACT_ADDRESS, contractABI, signer);
const result = await contract.somePublicViewFunction();
setData(result);
};
useEffect(() => {
if (account) {
fetchData();
}
}, [account]);
return (
<div>
<h1>Smart Contract Integration</h1>
{account ? (
<div>
<p>Connected Account: {account}</p>
<p>Data from Contract: {data}</p>
</div>
) : (
<button onClick={connectWallet}>Connect Wallet</button>
)}
</div>
);
};
export default App;
10. Reusable Snippets
Switch to Specific Network
const switchNetwork = async () => {
const targetChainId = "0x1"; // Example: Ethereum Mainnet (0x1)
try {
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: targetChainId }],
});
console.log("Network switched successfully!");
} catch (switchError) {
console.error("Error switching network:", switchError);
}
};
Convert Units
import { ethers } from "ethers";
// Convert Ether to Wei
const wei = ethers.utils.parseEther("1.0");
// Convert Wei to Ether
const ether = ethers.utils.formatEther(wei);
11. Testing with Hardhat/Local Blockchain
Set Up Hardhat Local Node
npx hardhat node
Deploy and Test Smart Contract Locally
- Use
localhost
provider URL:
const provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545");
12. Best Practices
- Use Environment Variables: Never hardcode sensitive data.
- Handle Errors Gracefully: Use
try-catch
for error handling. - Optimize Gas Costs: Avoid unnecessary operations in smart contracts.
- Test Thoroughly: Use tools like Hardhat or Truffle for testing.
Cheat Sheet: Deploying Smart Contracts
1. Tools Setup
Install Dependencies
npm install --save-dev hardhat ethers dotenv
Initialize Hardhat
npx hardhat
# Select "Create a basic sample project" and follow the prompts.
Set Up .env
File
Store sensitive information in a .env
file:
PRIVATE_KEY=<Your_Private_Key>
INFURA_API_URL=<Your_Infura_or_Alchemy_URL>
CONTRACT_NAME=<Your_Contract_Name>
Install the dotenv
package to load environment variables:
npm install dotenv
2. Write Your Smart Contract
Place your Solidity file in the contracts
folder (e.g., MyContract.sol
).
Example Contract (MyContract.sol
):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyContract {
string public message;
constructor(string memory _message) {
message = _message;
}
function updateMessage(string memory _newMessage) public {
message = _newMessage;
}
}
3. Compile the Contract
Use Hardhat to compile the contract:
npx hardhat compile
4. Deployment Script
Create a deploy.js
script in the scripts
folder.
Example Deployment Script (scripts/deploy.js
):
require("dotenv").config();
const { ethers } = require("hardhat");
async function main() {
// Load the contract to deploy
const Contract = await ethers.getContractFactory(process.env.CONTRACT_NAME);
// Deploy the contract
const contract = await Contract.deploy("Hello, Blockchain!");
console.log("Deploying contract...");
// Wait for deployment to complete
await contract.deployed();
console.log(`Contract deployed to: ${contract.address}`);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error("Error during deployment:", error);
process.exit(1);
});
5. Deploy the Contract
Run the deployment script:
npx hardhat run scripts/deploy.js --network <network_name>
Example Network Configurations (hardhat.config.js
):
require("@nomiclabs/hardhat-ethers");
require("dotenv").config();
module.exports = {
solidity: "0.8.0",
networks: {
hardhat: {}, // Local development
goerli: { // Goerli Testnet
url: process.env.INFURA_API_URL,
accounts: [`0x${process.env.PRIVATE_KEY}`],
},
mainnet: { // Ethereum Mainnet
url: process.env.INFURA_API_URL,
accounts: [`0x${process.env.PRIVATE_KEY}`],
},
},
};
6. Verify the Contract
Verify the contract on Etherscan or BSCScan using a plugin:
npm install --save-dev @nomiclabs/hardhat-etherscan
Add Etherscan API Key to .env
:
ETHERSCAN_API_KEY=<Your_Etherscan_API_Key>
Update hardhat.config.js
:
require("@nomiclabs/hardhat-etherscan");
module.exports = {
// ... other configurations
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
};
Run verification command:
npx hardhat verify --network <network_name> <contract_address> "constructor_argument"
7. Deploy Locally for Testing
Start a Local Blockchain
npx hardhat node
Deploy Locally
npx hardhat run scripts/deploy.js --network localhost
8. Reusable Deployment Commands
Run Tests
npx hardhat test
Compile Contracts
npx hardhat compile
Deploy to Testnet
npx hardhat run scripts/deploy.js --network goerli
Deploy to Mainnet
npx hardhat run scripts/deploy.js --network mainnet
9. Example Contract Interaction Post Deployment
Use a script to interact with the deployed contract:
require("dotenv").config();
const { ethers } = require("hardhat");
async function main() {
const contractAddress = "<Deployed_Contract_Address>";
const Contract = await ethers.getContractAt(process.env.CONTRACT_NAME, contractAddress);
// Example interaction
const message = await Contract.message();
console.log("Initial Message:", message);
const tx = await Contract.updateMessage("New Message!");
await tx.wait();
console.log("Message updated successfully.");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error("Error interacting with contract:", error);
process.exit(1);
});
10. Best Practices
- Gas Optimization: Optimize your contract code for lower gas fees.
- Test Thoroughly: Use unit tests in Hardhat to test all functionalities.
- Use Environment Variables: Never hardcode private keys or API URLs.
- Check Compatibility: Ensure your contract works across different EVM-compatible networks.
- Log Outputs: Always log deployment addresses for future reference.