I am working on a simple DApp voting project . And I am using react for the front-end. As I am new to react I am facing problem with the use navigator. On my project I have two different components , candidate validator and voter validator which is assigned at the time of contract deployment and those are public state variables.
On the homepage , When I connect wallet , the components will be rendered based on the wallet address . Suppose, I am connecting as the owner , then after connection ,I'll be rendered to the owner component and same goes for other components. But I'm facing problem with the candidate validator and voter validator components. Sometimes when I connect wallet as a candidate validator , it redirects to voter validator page and vice versa.
Here is my contract:
// SPDX-License-Identifier: MIT
import "hardhat/console.sol";
pragma solidity ^0.8.10;
contract voting {
address public owner;
address public canValidator;
address public voterValidator;
//the deployer of the contract is the owner of the contract
constructor(address validatorAdd, address _votervalidator) {
owner = msg.sender;
canValidator = validatorAdd;
voterValidator = _votervalidator;
}
// Candidate information
struct canInfo {
address addr;
string canName;
uint256 totalVote;
bool isVerifiedCan;
}
mapping(address => canInfo) public candidateInfo;
//array to store candidate address
address[] internal VcanAddress;
function addrCandidate(address _addr, string memory _Canname) external {
require(owner == msg.sender, "Only owner can add candidates");
require(_addr != address(0), "Address is not valid");
require(
candidateInfo[_addr].addr == address(0),
"Address is already added"
);
candidateInfo[_addr].addr = _addr;
candidateInfo[_addr].canName = _Canname;
VcanAddress.push(_addr);
}
//candidate verification//
//this function only can be accessed by candidate validator
//so should add a modifier or do the work inside function
function verifyCandidate(address canADD) external {
require(
msg.sender == canValidator,
"Only the candidate validator can access this function"
);
require(
!candidateInfo[canADD].isVerifiedCan,
"The candidate is not listed."
);
candidateInfo[canADD].isVerifiedCan = true;
}
//voter information
struct voteInfo {
address voteraddress;
string voterName;
string NID;
bool isVerifiedvoter;
bool hasVoted;
}
address[] internal allvoterAddr;
mapping(address => voteInfo) public voterInfo;
function addrVoter(
address _voterAddress,
string memory _name,
string memory _NID
) external {
require(owner == msg.sender, "Only owner can add candidates");
require(_voterAddress != address(0), "Address is not valid");
require(
voterInfo[_voterAddress].voteraddress == address(0),
"Address is already added"
);
voterInfo[_voterAddress].voteraddress = _voterAddress;
voterInfo[_voterAddress].voterName = _name;
voterInfo[_voterAddress].NID = _NID;
allvoterAddr.push(_voterAddress);
}
//function to verify voter
function verifyVoter(address voterADD) external {
require(
msg.sender == voterValidator,
"Only the voter validator can access this function"
);
require(
!voterInfo[voterADD].isVerifiedvoter,
"The voter is not listed."
);
voterInfo[voterADD].isVerifiedvoter = true;
}
/* Only the verified voters can vote the verified candidates */
function vote(address _candidate) external {
require(voterInfo[msg.sender].isVerifiedvoter, "Voter is not verified");
require(
candidateInfo[_candidate].isVerifiedCan,
"Candidate is not verified"
);
require(!voterInfo[msg.sender].hasVoted, "Voter already voted.");
candidateInfo[_candidate].totalVote += 1;
voterInfo[msg.sender].hasVoted = true;
}
function winnerCandidate() public view returns (address, uint256) {
require(
msg.sender == owner,
"Function caller is not owner of the contract."
);
uint256 tempcount = 0;
address tempaddress = address(0);
for (uint256 i = 0; i < VcanAddress.length; i++) {
if (candidateInfo[VcanAddress[i]].totalVote > tempcount) {
tempcount = candidateInfo[VcanAddress[i]].totalVote;
tempaddress = VcanAddress[i];
}
}
return (tempaddress, tempcount);
}
function getAllVoterInfo() public view returns (voteInfo[] memory) {
voteInfo[] memory allVoters = new voteInfo[](allvoterAddr.length);
for (uint256 i = 0; i < allvoterAddr.length; i++) {
allVoters[i] = voterInfo[allvoterAddr[i]];
}
return allVoters;
}
function getAllCandidateInfo() public view returns (canInfo[] memory) {
canInfo[] memory allCandidates = new canInfo[](VcanAddress.length);
for (uint256 i = 0; i < VcanAddress.length; i++) {
allCandidates[i] = candidateInfo[VcanAddress[i]];
}
return allCandidates;
}
}
This is home.jsx:
import React, { useState, useEffect } from "react";
import { ethers } from "ethers";
import { Routes, Route } from "react-router-dom";
import { useNavigate } from "react-router-dom";
const Home = ({ state, account }) => {
const navigate = useNavigate();
const { contract } = state;
const [owner, setOwner] = useState("");
const [canValidators, setcanValidators] = useState("");
const [voterValidators, setvoterValidators] = useState("");
const [voters, setVoters] = useState([]);
const [candidates, setCandidates] = useState([]);
useEffect(() => {
async function getOwner() {
const ownerAddress = await contract.owner();
setOwner(ownerAddress);
}
async function getCanValidators() {
const canValidator = await contract.canValidator();
setcanValidators(canValidator);
}
async function getVoterValidators() {
const voterValidator = await contract.voterValidator();
setvoterValidators(voterValidator);
}
async function getVoters() {
const voters = await contract.getAllVoterInfo();
setVoters(voters);
}
async function getCandidates() {
const candidates = await contract.getAllCandidateInfo();
setCandidates(candidates);
}
if (contract) {
getOwner();
getCanValidators();
getVoterValidators();
getVoters();
getCandidates();
}
}, [contract]);
useEffect(() => {
const allVoters = voters.flat();
const allCandidates = candidates.flat();
///Debugging
console.log(
"Before navigation\n",
"Candidate validator" + canValidators + "\n",
"Voter validator" + voterValidators + "\n",
"account" + account
);
if (account) {
navigate(
account === owner
? "/owner"
: voterValidators
? "/VoterValidator"
: canValidators
? "/CanValidator"
: allVoters.includes(account)
? "/voter"
: allCandidates.includes(account)
? "/candidate"
: "/",
{ state: { isAccount: true } }
);
///Debugging
console.log(
"After navigation" + "\n",
"Candidate validator" + canValidators + "\n",
"Voter validator" + voterValidators + "\n",
"account" + account
);
}
}, [
account,
navigate,
owner,
canValidators,
voterValidators,
voters,
candidates,
]);
return (
<div className="container-fluid d-flex justify-content-center align-items-center py-5">
<h1>This is the home page</h1>
</div>
);
};
export default Home;
This is app.jsx:
import { useState, useEffect } from "react";
import { ethers } from "ethers";
import { Routes, Route } from "react-router-dom";
import abi from "../../artifacts/contracts/voting.sol/voting.json";
import "./App.css";
import Home from "./components/home";
import Owner from "./components/owner";
import ValidatorCan from "./components/canvalidator";
import ValidatorVoter from "./components/votervalidator";
import Voter from "./components/voter";
import Candidate from "./components/candidate";
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
const [state, setState] = useState({
provider: null,
signer: null,
contract: null,
});
const [account, setAccount] = useState("None");
const [isConnected, setIsConnected] = useState(false);
const [hovered, setHovered] = useState(false);
const connectWallet = async () => {
const contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
const contractABI = abi.abi;
try {
const { ethereum } = window;
if (ethereum) {
await ethereum.request({ method: "eth_requestAccounts" });
window.ethereum.on("chainChanged", () => {
window.location.reload();
});
window.ethereum.on("accountsChanged", () => {
window.location.reload();
});
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(
contractAddress,
contractABI,
signer
);
const accounts = await provider.listAccounts();
setAccount(accounts[0]);
setState({ provider, signer, contract });
setIsConnected(true);
} else {
alert("Please install metamask");
}
} catch (error) {
console.log(error);
}
};
const [allowedRoutes, setAllowedRoutes] = useState([
{ path: "/", element: <Home state={state} account={account} /> },
]);
useEffect(() => {
async function checkAllowedRoutes() {
if (account === "None") {
setAllowedRoutes([
{ path: "/", element: <Home state={state} account={account} /> },
]);
return;
}
const { contract } = state;
const canValidator = await contract.canValidator();
const voterValidator = await contract.voterValidator();
const allVoters = await contract.getAllVoterInfo();
const allCandidates = await contract.getAllCandidateInfo();
const allowedRoutes = [
{ path: "/", element: <Home state={state} account={account} /> },
];
if (account === (await contract.owner())) {
allowedRoutes.push({
path: "/owner",
element: <Owner state={state} />,
});
} else if (canValidator === account) {
allowedRoutes.push({
path: "/CanValidator",
element: <ValidatorCan state={state} />,
});
} else if (voterValidator === account) {
allowedRoutes.push({
path: "/VoterValidator",
element: <ValidatorVoter state={state} />,
});
} else if (allVoters.flat().includes(account)) {
allowedRoutes.push({
path: "/voter",
element: <Voter state={state} />,
});
} else if (allCandidates.flat().includes(account)) {
allowedRoutes.push({
path: "/candidate",
element: <Candidate state={state} />,
});
}
setAllowedRoutes(allowedRoutes);
}
checkAllowedRoutes();
}, [account, state]);
return (
<div className="container">
{isConnected ? (
<button
className="connect-wallet float-end"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{hovered ? `connected account:${account}` : "Connected"}
</button>
) : (
<button className="connect-wallet float-end" onClick={connectWallet}>
Connect Wallet
</button>
)}
<Routes>
{allowedRoutes.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
</Routes>
</div>
);
}
export default App;
When I connect as a candidate validator two things might happen,
When I connect as a voter validator two things might happen,
It seems that the voter validator address sometimes gets empty. How to resolve this problem ? [N.B : I am using hardhat local nodes and kite]