Skip to content

Migrate smart contract development environment from Hardhat to Foundry#225

Open
Basharkhan7776 wants to merge 3 commits intoAOSSIE-Org:mainfrom
Basharkhan7776:issue-#149
Open

Migrate smart contract development environment from Hardhat to Foundry#225
Basharkhan7776 wants to merge 3 commits intoAOSSIE-Org:mainfrom
Basharkhan7776:issue-#149

Conversation

@Basharkhan7776
Copy link

@Basharkhan7776 Basharkhan7776 commented Jan 27, 2026

Issue #149

Summary

This PR completely migrates the blockchain development environment from Hardhat to Foundry. It replaces the JavaScript-based testing and deployment workflow with a faster, Solidity-native Foundry setup. All existing tests have been ported to Solidity, and the project structure has been updated accordingly.

Changes

Configuration

  • Initialized Foundry: Added foundry.toml configuration.
  • Dependencies: Replaced npm dependencies with native Forge libraries (openzeppelin-contracts, chainlink-brownie-contracts) in lib/.
  • Remappings: Configured remappings.txt for accurate import resolution.
  • Cleanup: Removed hardhat.config.js, package.json, node_modules, and the ignition/ directory.

Tests

  • Ported Tests: Converted all JavaScript tests (Election.test.js, ElectionFactory.test.js) to Solidity tests (.t.sol files) located in test/.
  • Coverage: Maintained full test coverage for all voting strategies (General, Ranked, IRV, Schulze, Kemeny-Young).
  • Optimization: Flattened the test directory structure for better discovery.

DevOps

  • Dockerfile: Updated to use the official ghcr.io/foundry-rs/foundry:latest image, optimizing the build container for Foundry.
  • Git: Updated .gitignore to track Foundry artifacts (out, broadcast, cache_forge) correctly and ignore legacy Hardhat files.

Documentation

  • README: Updated README.md with instructions for building, testing, and deploying using Foundry (forge build, forge test, anvil).

Verification

  • Run forge test to execute the full test suite (15 passing tests).
  • Run forge build to verify contract compilation.

Summary by CodeRabbit

  • Chores

    • Migrated tooling from Hardhat to Foundry (configs, Docker image, ignore/remappings) and removed legacy Hardhat artifacts and deployment modules.
  • Tests

    • Replaced JS/Mocha suites with comprehensive Solidity/Foundry tests covering Election and ElectionFactory flows.
  • Documentation

    • README rewritten for Foundry: prerequisites, build, test, local node, and deploy instructions.
  • Contracts

    • Reworked factory and cross-chain messaging interfaces and added mock router for Foundry workflows.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

📝 Walkthrough

Walkthrough

Migrate the blockchain workspace from a Hardhat/JS setup to Foundry: replace tooling, Docker, and package config; remove Hardhat Ignition modules, JS tests, and artifacts; add Foundry config, remappings, Solidity tests, and refactor contracts and CCIP flows.

Changes

Cohort / File(s) Summary
Tooling & ignore
blockchain/.gitignore, blockchain/foundry.toml, blockchain/remappings.txt
Add Foundry config and remappings; update .gitignore for Foundry outputs, dependencies, and misc files.
Container & package
blockchain/Dockerfile, blockchain/package.json
Replace Node/Hardhat base image and build steps with Foundry image and Forge workflow; simplify package.json to a lighter manifest.
Documentation
blockchain/README.md
Replace Hardhat-centric README with Foundry-focused getting started, build/test/format/deploy instructions and reorganized sections.
Hardhat config removed
blockchain/hardhat.config.js
Delete Hardhat configuration and associated network/compiler/plugin settings.
Ignition modules & deployments removed
blockchain/ignition/modules/*, blockchain/ignition/deployments/...
Remove Hardhat Ignition modules, deployment artifacts, debug files, journals, and deployed address mappings for affected chains.
JS tests & fixtures removed
blockchain/test/*.js, blockchain/test/fixtures/*.js, blockchain/test/unit/*
Delete Mocha/Hardhat JavaScript tests and fixtures covering Election/ElectionFactory and voting algorithm suites.
Foundry Solidity tests added
blockchain/test/Election.t.sol, blockchain/test/ElectionFactory.t.sol
Add Forge-based Solidity tests exercising candidate management, voting flows, result calculations, and factory behaviors.
Contracts refactor & CCIP
blockchain/contracts/ElectionFactory.sol, blockchain/contracts/cross-chain/CCIPSender.sol, blockchain/contracts/mocks/MockCCIPReceiverRouter.sol
Refactor ElectionFactory to owner/constructor/clone model with CCIP vote handling; update CCIP sender and mock router imports, formatting, and send/receive flows.
Artifacts removed
blockchain/ignition/deployments/.../artifacts/*.json, .../*.dbg.json
Delete compiled artifact and debug JSON files for multiple deployed contracts.

Sequence Diagram(s)

sequenceDiagram
  participant User as User/Caller
  participant Factory as ElectionFactory
  participant Generator as BallotGenerator / ElectionGenerator
  participant Cloner as Clones
  participant Election as Election Instance
  participant Result as ResultCalculator

  User->>Factory: createElection(electionInfo, candidates, ballotType, resultType)
  Factory->>Generator: generateBallot(electionInfo, ballotType)
  Factory->>Cloner: clone(electionGenerator)
  Cloner-->>Factory: newElectionAddress
  Factory->>Election: initialize(newElectionAddress, electionInfo, candidates, ballot, resultType)
  Election-->>Factory: initialized
  Factory-->>User: return electionAddress / update open list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

"🐰
Foundry forged at dawn, I hop with cheer—
Tests in Solidity, the path is clear.
Old JS leaves, new anvil hums along,
Ballots cloned and CCIP sings my song."

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: migrating the smart contract development environment from Hardhat to Foundry, which is the primary objective of this comprehensive changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@blockchain/.gitignore`:
- Line 6: The repo ignores lib/ but foundry.toml sets libs = ["lib",
"node_modules"] and remappings.txt expects dependencies under lib/, which breaks
builds for fresh clones; either remove lib from .gitignore and track the
dependency folders (or add them as git submodules) so remappings like
`@openzeppelin/contracts/`=lib/openzeppelin-contracts/contracts/ resolve, OR keep
lib ignored but add a clear README setup step instructing contributors to run
forge install after cloning (or provide a script) to fetch dependencies into
lib/ so remappings and builds succeed.

In `@blockchain/remappings.txt`:
- Line 5: The remapping entry "forge-std/=forge_std/src/" is invalid because the
directory doesn't exist; either remove this remapping from
blockchain/remappings.txt if forge-std isn't used, or correct it to the standard
Foundry path "forge-std/=lib/forge-std/src/" and ensure the dependency is added
to lib/ so the compiler can resolve it; update the remapping line and verify
compilation succeeds.
- Line 4: The remapping entry referencing ds-test points to a non-existent
nested path
(ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/); either add
the dependency in that exact location or update the remapping to a valid ds-test
installation path. Fix by either (A) adding ds-test as a git submodule or
dependency so the directory lib/ds-test/src/ exists and change the remapping to
ds-test/=lib/ds-test/src/, or (B) if you installed ds-test elsewhere (e.g.,
lib/forge-std/lib/ds-test/src/ or node_modules/ds-test/src/), update the
remapping to that actual path; locate the entry in remappings.txt and replace
the invalid path with the correct one or remove it if ds-test is not used.
🧹 Nitpick comments (11)
blockchain/foundry.toml (2)

4-4: Consider removing node_modules from libs.

Since this PR migrates away from npm/Hardhat, including node_modules in the libs array is inconsistent with the new tooling approach. If npm dependencies are truly removed, this entry is unnecessary and could cause confusion.

-libs = ["node_modules", "lib"]
+libs = ["lib"]

16-17: Missing etherscan configuration for amoy network.

The [rpc_endpoints] section defines both sepolia and amoy, but the [etherscan] section only configures sepolia. If contract verification on Polygon Amoy is needed, add the corresponding config:

 [etherscan]
 sepolia = { key = "${ETHERSCAN_KEY}" }
+amoy = { key = "${POLYGONSCAN_KEY}" }
blockchain/test/ElectionFactory.t.sol (2)

4-4: Remove unused console import.

The console is imported but never used in the test file.

-import {Test, console} from 'forge-std/Test.sol';
+import {Test} from 'forge-std/Test.sol';

98-110: Add end-to-end test to verify whitelist state changes.

The test only confirms that owner calls don't revert, but doesn't verify the whitelist was actually modified. While the approvedSenderContracts mapping lacks a public getter, you can test it indirectly through the _ccipReceive function, which validates incoming messages against the whitelist. Add a test that:

  1. Whitelists a contract for a chain
  2. Calls _ccipReceive with that sender (should succeed)
  3. Removes the contract from the whitelist
  4. Calls _ccipReceive with that same sender (should revert with NotWhitelistedSender)

This verifies that add/remove operations actually modify the whitelist state.

blockchain/README.md (3)

47-50: Use markdown link syntax for URLs.

Per markdown best practices, wrap bare URLs in angle brackets or use proper link syntax.

-- Election Factory - https://sepolia.etherscan.io/address/0x64c720eBD5227bba57a5E8282893e71087cCcBb8#code
+- Election Factory - <https://sepolia.etherscan.io/address/0x64c720eBD5227bba57a5E8282893e71087cCcBb8#code>
 - CCIP Sender :-
-  - Fuji - https://testnet.snowtrace.io//address/0xf267f0603672E791779E52B153BD9A39C9928767#code
+  - Fuji - <https://testnet.snowtrace.io//address/0xf267f0603672E791779E52B153BD9A39C9928767#code>

7-9: Consider adding dependency installation instructions.

If lib/ is gitignored (as shown in .gitignore), users will need to run forge install after cloning. Consider adding this to the prerequisites or as a setup step:

 ### Prerequisites

 - [Foundry](https://book.getfoundry.sh/getting-started/installation)
+
+### Install Dependencies
+
+```shell
+forge install
+```

42-43: Clarify or update the deploy script example.

The example references script/MyScript.s.sol, which does not exist in the repository. Either update the path to an actual deployment script if one exists, or add a note explaining that users should create their own script following Foundry conventions.

blockchain/Dockerfile (1)

1-1: Pin the Foundry image to a specific version for reproducible builds.

Using :latest is a moving tag that tracks the development branch and can cause unexpected build failures when the upstream image changes. Consider pinning to a stable release or an immutable tag:

-FROM ghcr.io/foundry-rs/foundry:latest
+FROM ghcr.io/foundry-rs/foundry:stable

Or use an immutable nightly tag (e.g., nightly-<git_sha>) for more frequent updates while maintaining reproducibility.

blockchain/test/Election.t.sol (3)

4-4: Unused import: console is never used.

The console import from forge-std/Test.sol is not utilized in any test. Remove it to keep imports clean.

Suggested fix
-import {Test, console} from 'forge-std/Test.sol';
+import {Test} from 'forge-std/Test.sol';

46-52: Minor typo in test fixture data.

Line 50 has 'candidate2s' which appears to be a typo for 'candidate2'. While this doesn't affect test correctness, fixing it improves consistency.

Suggested fix
     mockCandidates.push(
       Election.Candidate({
         candidateID: 2,
         name: 'candidate2',
-        description: 'candidate2s'
+        description: 'candidate2'
       })
     );

251-287: Minor style: Missing blank line before function.

Add a blank line before test_ResultCalculation_Schulze() for consistency with other test functions.

Suggested fix
     assertEq(winners[0], 1);
   }
+
   function test_ResultCalculation_Schulze() public {

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@blockchain/contracts/ElectionFactory.sol`:
- Around line 59-95: createElection currently uses electionCount as the
election's stable ID but stores ownership keyed by the array index
(electionOwner[openBasedElections.length]), and deleteElection uses swap-and-pop
on openBasedElections which breaks the ID-to-index mapping; change ownership and
deletion to use the stable election ID instead: assign the ID (electionCount) to
the Election via Election.initialize, store ownership keyed by that ID
(electionOwner[electionId] = msg.sender), push the election address to
openBasedElections and also maintain a mapping electionIdToIndex (and optionally
indexToElectionId) that records the array index for each electionId, then update
deleteElection to look up the victim's array index via electionIdToIndex,
perform swap-and-pop on openBasedElections while updating both electionIdToIndex
entries and electionOwner by electionId (not by array index), and increment
electionCount after initialization; update all references to use electionId
mappings so swap-and-pop no longer corrupts ID correspondence.
🧹 Nitpick comments (3)
blockchain/package.json (2)

4-12: Replace the placeholder npm test script (and drop markdown in description).

npm test currently always fails, which can trip up default CI/dev workflows; wire it to your Foundry test command or remove it. Also npm descriptions are plain text, so the **Foundry** markdown will render literally.

♻️ Proposed update
-  "description": "This project contains the smart contracts for Agora Blockchain, built using **Foundry**.",
+  "description": "This project contains the smart contracts for Agora Blockchain, built using Foundry.",
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
+  "scripts": {
+    "test": "forge test"
+  },

17-20: Remove unused lib reference from foundry.toml for clarity.

The lib/ directory doesn't exist, and all remappings in remappings.txt consistently point to node_modules/. Consider removing "lib" from the libs array in foundry.toml (line 4) to avoid confusion about dependency sources.

blockchain/contracts/mocks/MockCCIPReceiverRouter.sol (1)

37-40: Prefer selector constant over hard‑coded signature.

Using ElectionFactory.ccipReceive.selector avoids signature drift if the struct layout changes.

♻️ Proposed refactor
-    bytes4 selector = bytes4(
-      keccak256('ccipReceive((bytes32,uint64,bytes,bytes,(address,uint256)[]))')
-    );
-    bytes memory callData = abi.encodeWithSelector(selector, message);
+    bytes memory callData = abi.encodeWithSelector(
+      ElectionFactory.ccipReceive.selector,
+      message
+    );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant