Optimize Gas Usage and Build Efficient Smart Contracts on CrossFi
Azeez Abidoye

Azeez Abidoye @azeezabidoye

About: A software and blockchain developer with over five years of experience. I've collaborated with DLT Africa, Bitmama, Celo Foundation, and Morph, and I currently serve as a Developer Advocate at CrossFi

Location:
Lagos, Nigeria
Joined:
Mar 27, 2024

Optimize Gas Usage and Build Efficient Smart Contracts on CrossFi

Publish Date: Jun 26
18 4

What is Gas Optimization? 🤷‍♂️

Optimizing gas in smart contracts means writing Solidity code and structuring contracts in ways that reduce the amount of gas (computation cost) needed to deploy and execute them. Since every transaction or operation on a blockchain requires gas — paid in the network's native token — optimization helps make your applications faster, cheaper, and more scalable.

What It’s Like to Optimize Gas ⚙️

Think of it as performance tuning for the blockchain:

  • You analyze how your contract reads/writes data.

  • You replace costly patterns with cheaper alternatives.

  • You reduce redundancy in logic and storage.

  • You measure how much gas is used before and after optimizations (with tools like hardhat-gas-reporter or solidity-coverage).

Why CrossFi? 🤔

CrossFi is a blockchain platform built to enable seamless cross-chain asset transfers and decentralized finance (DeFi) applications. With a focus on interoperability, security, and scalability, CrossFi provides developers and users with a powerful ecosystem for deploying decentralized applications (DApps) that can operate across multiple blockchain networks.

Prerequisites 📚

  • NodeJs
  • NPM
  • MetaMask
  • Testnet Tokens
  • Coinmarketcap API
  • CrossFi Alchemy URL

Dev Tools ⚒️

  • Yarn
npm install yarn -g
Enter fullscreen mode Exit fullscreen mode

Step 1: Create & open project directory

mkdir fundxfi && cd fundxfi
Enter fullscreen mode Exit fullscreen mode

Step 2: Initialize a new project

yarn init -y
Enter fullscreen mode Exit fullscreen mode

Step 3: Install Hardhat package

yarn add hardhat
Enter fullscreen mode Exit fullscreen mode

Step 4: Initialize new Hardhat framework

yarn hardhat init
Enter fullscreen mode Exit fullscreen mode

✍️ Choose JavaScript and install other dependencies

Step 5: Set up ENV

  • Create a new .env file in the root directory
  • Populate it with the following variables:
PRIVATE_KEY="insert-private-key-here"
CROSSFI_ALCHEMY_URL="your-crossfi-alchemy-url-here"
CONINMARKET_API_KEY="add-coin-market-cap-api-here"
Enter fullscreen mode Exit fullscreen mode
  • Install a new package for .env configuration
yarn add dotenv -D
Enter fullscreen mode Exit fullscreen mode

Step 6: [Optional] Delete old files

✍️ To keep a clean slate, delete contracts/Lock.sol and test/lock.js files

Step 7: Configure framework

  • Rename hardhat.config.js file to hardhat.config.cjs
  • List and configure all imports
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
require("hardhat-gas-reporter");
require("solidity-coverage");
Enter fullscreen mode Exit fullscreen mode
  • Import environment variables
const { PRIVATE_KEY, CROSSFI_ALCHEMY_URL } = process.env;
Enter fullscreen mode Exit fullscreen mode
  • Configure network

module.exports = {
  solidity: "0.8.28",
  networks: {
    hardhat: { chainId: 1337 },
    crossfiTestnet: {
      url: CROSSFI_ALCHEMY_URL,
      accounts: [PRIVATE_KEY],
      chainId: 4157,
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Step 8: Create Solidity smart contract

  • Navigate to ./contracts directory
  • Create new FundXFI.sol file
  • Populate with the following code:
  • Unoptimized Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract FundXFI {
    address public owner;
    uint256 public minimumAmount = 20 * 1e18; // ETH to Wei
    mapping(address => uint256) public addressToAmountFunded;
    mapping(address => bool) private hasFunded;
    address[] private funders;

    constructor() {
        owner = msg.sender;
    }

    function fund() external payable {
        require(msg.value > minimumAmount, "Send some ETH");

        if (!hasFunded[msg.sender]) {
            hasFunded[msg.sender] = true;
            funders.push(msg.sender);
        }

        addressToAmountFunded[msg.sender] += msg.value;
    }

    function withdraw() public onlyOwner {
        uint256 len = funders.length;

        for (uint256 i = 0; i < len; i++) {
            address funder = funders[i];
            addressToAmountFunded[funder] = 0;
            hasFunded[funder] = false;
            unchecked {
                ++i;
            } // Save gas by skipping overflow check
        }

        funders = new address[](0);
        payable(owner).transfer(address(this).balance);
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    function getFunders() external view returns (address[] memory) {
        return funders;
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 9: Compile Contract

yarn hardhat compile
Enter fullscreen mode Exit fullscreen mode

Result:

Compiled 1 Solidity file successfully (evm target: paris).
✨ Done in 3.15s.
Enter fullscreen mode Exit fullscreen mode

Step 10: Setup Contract for deployment

  • Create new directory for deployment in the root directory
mkdir scripts
Enter fullscreen mode Exit fullscreen mode
  • Create new deploy.cjs file in the ./scripts directory
  • Populate with the following code:
const { ethers } = require("hardhat");

async function main() {
  // Get the contract factory
  const FundXFI = await ethers.getContractFactory("FundXFI");

  console.log("Deploying contract...");
  const fundxfi = await FundXFI.deploy(); // No constructor args needed
  await fundxfi.waitForDeployment();
  const fundxfiAddr = await fundxfi.getAddress();

  console.log(`✅ Contract deployed at: ${fundxfiAddr}`);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error("❌ Deployment failed:", error);
    process.exit(1);
  });
Enter fullscreen mode Exit fullscreen mode

Step 11: Deploy Contract on CrossFi Testnet

yarn hardhat run scripts/deploy.cjs --network crossfiTestnet 
Enter fullscreen mode Exit fullscreen mode

Result:

Deploying contract...
✅ Contract deployed at: 0x7bEf72A46D8f0af065fC6Dd96e3048ABC6924E45
✨ Done in 33.35s.
Enter fullscreen mode Exit fullscreen mode

Bonus: 🎁 Gas Optimization Tools

Solidity Coverage:

yarn hardhat coverage
Enter fullscreen mode Exit fullscreen mode

Result:

Version
=======

> solidity-coverage: v0.8.16

# Instrumenting for coverage...

> FundXFI.sol

# Compilation:

Compiled 1 Solidity file successfully (evm target: paris).

# Network Info

> HardhatEVM: v2.25.0
> network: hardhat

0 passing (0ms)

-----------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-----------------|----------|----------|----------|----------|----------------|
contracts/ | 0 | 0 | 0 | 0 | |
FundXFI.sol | 0 | 0 | 0 | 0 |... 41,45,46,50 |
-----------------|----------|----------|----------|----------|----------------|
All files | 0 | 0 | 0 | 0 | |
-----------------|----------|----------|----------|----------|----------------|

> Istanbul reports written to ./coverage/ and ./coverage.json
> ✨ Done in 5.33s.

Enter fullscreen mode Exit fullscreen mode

Hardhat Gas Reporter:

✍️ See documentation for more guidance

  • Get API Key and update variable in the .env file
  • Import API key in the hardhat.config.cjs file
const { PRIVATE_KEY, CROSSFI_ALCHEMY_URL, CONINMARKET_API_KEY } = process.env;
Enter fullscreen mode Exit fullscreen mode
  • Update config as follows:
module.exports = {
  // networks...
    gasReporter: {
      enabled: true,
      currency: "USD",
      coinmarketcap: COINMARKETCAP_API_KEY, 
      showTimeSpent: true,
      excludeContracts: [],
      noColors: false,
    },
};
Enter fullscreen mode Exit fullscreen mode
  • To view gas analytics report
yarn hardhat test
Enter fullscreen mode Exit fullscreen mode
  • Replace the ./contracts/FundXFI.sol with the following code:
  • Optimized Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract FundXFI {
    address public immutable i_owner;
    uint256 public constant MINIMUM_AMOUNT = 20 * 1e18; // ETH to Wei
    mapping(address => uint256) public addressToAmountFunded;
    mapping(address => bool) private hasFunded;
    address[] private funders;

    constructor() {
        i_owner = msg.sender;
    }

    function fund() external payable {
        require(msg.value > MINIMUM_AMOUNT, "Send some ETH");

        if (!hasFunded[msg.sender]) {
            hasFunded[msg.sender] = true;
            funders.push(msg.sender);
        }

        addressToAmountFunded[msg.sender] += msg.value;
    }

    function withdraw() external onlyOwner {
        for (uint256 i = 0; i < funders.length; i++) {
            address funder = funders[i];
            addressToAmountFunded[funder] = 0;
            hasFunded[funder] = false;
            unchecked {
                ++i;
            } // Save gas by skipping overflow check
        }

        delete funders;

        (bool success, ) = payable(i_owner).call{value: address(this).balance}(
            ""
        );
        require(success, "Withdraw failed");
    }

    modifier onlyOwner() {
        require(msg.sender == i_owner, "Not owner");
        _;
    }

    function getFunders() external view returns (address[] memory) {
        return funders;
    }
}
Enter fullscreen mode Exit fullscreen mode

APPROACH ♻️

  • Compile the smart contract
yarn hardhat compile
Enter fullscreen mode Exit fullscreen mode
  • Deploy the contract to the CrossFi Testnet
yarn hardhat run scripts/deploy.cjs --network crossfiTestnet
Enter fullscreen mode Exit fullscreen mode
  • Run tests to view the gas analytics report
yarn hardhat test
Enter fullscreen mode Exit fullscreen mode
  • Compare report

Efficiency Techniques 📊

Techniques Purpose
Use immutable and constant Save storage slot reads
Replace transfer with call Avoid 2300 gas cap
Use external over public when possible Cheaper gas
Minimize state writes Especially inside loops
Avoid unbounded loops For scalability
Pack storage variables Reduces slot usage

TL;DR: 🧠

  • Optimizing gas = smart engineering
  • Advantage = happy users, lower fees, better performance
  • Why it matters = in blockchain, performance is paid for in tokens

Conclusion

🔗 Checkout: wwww.crossfi.org for more information.

Comments 4 total

Add comment