Solidity Smart Contract Coding, integration and deployment : Cheat Sheet

Solidity Smart Contract Coding, integration and deployment : Cheat Sheet

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.

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 and storage 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

  1. Use Environment Variables: Never hardcode sensitive data.
  2. Handle Errors Gracefully: Use try-catch for error handling.
  3. Optimize Gas Costs: Avoid unnecessary operations in smart contracts.
  4. 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

  1. Gas Optimization: Optimize your contract code for lower gas fees.
  2. Test Thoroughly: Use unit tests in Hardhat to test all functionalities.
  3. Use Environment Variables: Never hardcode private keys or API URLs.
  4. Check Compatibility: Ensure your contract works across different EVM-compatible networks.
  5. Log Outputs: Always log deployment addresses for future reference.

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *