Seeking feedback for a solidity project

Hello Everyone,

After Ethereum smart contract programming 101, I wanted to work on a small project… Where I can use a wallet similar to the one that we’d created during the course.
I’ve created a simple voting system using a multi-user wallet.

There may be some flaws in the code… maybe my implementation doesn’t work the way the actual system works… Still, here is a try.

Overview

Here in the project, I’m associating ethers present in a wallet with credits of a user that he/she can utilize for voting. For instance - 3 ethers correspond to 3 credits.

A user can express how strongly they feel about an issue by using voting points.
That is, a user can give voting points from the range of [-credits, credits] for each poll.
An admin can create a poll and perform other duties…


I would be highly appreciative if anybody could suggest how to improve the code further, any new feature, any other suggestions…

Thanks,
Tanu

Wallet.sol

pragma solidity 0.7.5;

contract Wallet{
    
    mapping(address => uint)balance;
    
    event AmountDeposited(address sender, uint amount);
    
    function deposit() public payable {
        balance[msg.sender] += msg.value;
        emit AmountDeposited(msg.sender,msg.value);
    }
    
    function withdraw(uint _amount) public {
        require(balance[msg.sender] >= _amount,'Withdrawal is more than a deposit');
        balance[msg.sender] -= _amount;
        msg.sender.transfer(_amount);
    }
    
    function transfer(address payable _recipient, uint _amount) public {
        require(balance[msg.sender] >= _amount,'Transfer is more than a deposit');
        balance[msg.sender] -= _amount;
        _recipient.transfer(_amount);
    }
    
    function getBalance(address _add) view external returns(uint) {
        return balance[_add];
    }
}

Admin.sol

pragma solidity 0.7.5;
pragma abicoder v2;

contract Admin{
    
    struct Poll{
        uint pollId;
        string pollDetails;
        uint256 creationTime;
        uint256 expirationTime; // in days
        uint voters;
        int votes;
        int result;
    }
    Poll [] polls;
    address admin;
    
    constructor(){
        admin = msg.sender;
    }
    modifier onlyAdmin{
        require(msg.sender == admin,'Accessible by Admin only');
        _;
    }
    
    mapping(uint => Poll)pollLogs; //pollId => poll
    
    event PollCreated(address creator,uint pollId, string pollDetails, uint validityInDays);
    
    //validityTime - Validity in terms of days
    function createPoll(string memory _pollDetails,uint validityTime) public onlyAdmin{
        Poll memory p = Poll(polls.length,_pollDetails,block.timestamp,block.timestamp + (validityTime * 1 days),0,0,0);
        pollLogs[p.pollId] = p;
        polls.push(p);
        emit PollCreated(admin,p.pollId,_pollDetails,validityTime);
    }
    
    function viewPoll(uint _pollId) public view returns(Poll memory){
        return pollLogs[_pollId];
    } 
    
    function getAllPolls() public view returns(Poll [] memory){
        return polls;
    }
    
    function pollResult(uint _pollId) public view returns(string memory){
        require(pollLogs[_pollId].voters != 0,'No one has voted in this poll');
        require(block.timestamp > pollLogs[_pollId].expirationTime,'Poll result can not be declared before due date');
         return  pollLogs[_pollId].result > 0 ?'Accepted':'Rejected';
         
    }
}

Voter.sol

pragma solidity 0.7.5;
pragma abicoder v2;

import './Admin.sol';

contract VoterPanel is Admin{
    
    struct VoterHistory{
        uint pollId;
        int votes;
    }
    struct Voter{
        uint credits;
        VoterHistory [] history;
    }
    
    mapping(address => Voter)voterLog;
    mapping(address => mapping(uint => bool))checkStatus; // address => pollId => T/F
    
    function voterDetails(address _add) public view returns(Voter memory){
        return voterLog[_add];
    }
}

VotingSystem.sol

pragma solidity 0.7.5;
pragma abicoder v2;
import './Voter.sol';

interface WalletInterface{
    function getBalance(address _add) view external returns(uint);
}

contract VotingPortal is VoterPanel{
    
    WalletInterface walletInstance;
    
    constructor(address walletAddress){
        walletInstance = WalletInterface(walletAddress);
    }
    
    event CreditsEarned(address _voter, uint _creditsEarned);
    
    function earnCredits () public returns(uint) {
        require(voterLog[msg.sender].credits == 0,'Use your remaining credits first');
        uint walletCredits = walletInstance.getBalance(msg.sender);
        require(walletCredits > 0,'To earn credits fill your wallet first');
        voterLog[msg.sender].credits = walletCredits/(1 ether);
        emit CreditsEarned(msg.sender,voterLog[msg.sender].credits);
        return voterLog[msg.sender].credits;
    }
    
    function viewCredits()public view returns(uint){
        return voterLog[msg.sender].credits;
    }
    
    function abs(int x) private pure returns (uint) {
        return x >= 0 ? uint(x) : uint(-x);
    }
    
    //votingPoints = [-credits,credits] , can be negative also
    function vote(uint _pollId,int votingPoints) public {
        
        require(checkStatus[msg.sender][_pollId] != true,"You've already voted for this poll");
        require(pollLogs[_pollId].expirationTime > block.timestamp , "This poll has expired");
        require(votingPoints != 0,'Voting Points can not be zero');
        require(abs(votingPoints) <= voterLog[msg.sender].credits,'Voting Points can not be more than credits');
        
        voterLog[msg.sender].credits = 0;
        voterLog[msg.sender].history.push(VoterHistory(_pollId,votingPoints));
        
        checkStatus[msg.sender][_pollId] = true;
        
        pollLogs[_pollId].voters++;
        pollLogs[_pollId].votes += votingPoints;
        pollLogs[_pollId].result = pollLogs[_pollId].votes/int(pollLogs[_pollId].voters);
        polls[_pollId] = pollLogs[_pollId];
    }
}
1 Like

This is great.
I’m not at a computer currently so I won’t be able to run the code but if you remind me later I’ll definitely check it out and provide feedback.
At a glance it looks good. :heart_eyes:

Thanks, @jak. Every piece of feedback is precious. :slightly_smiling_face:

Hey @tanu_g,

nice little project!

I’m not 100% sure I fully understand the intent of this voting system, but it looks to me the system allows users to earn credits from ether they hold in the contract wallet.
The user can then use some or all of these credits to vote for a poll.
Because the ether is not “spent” to earn those credits, the user can reuse the same ether to earn more credits and vote on a different poll.
Is that the intent?
It looks to me the system is well coded. I just left a couple of minor comments below.


Is it conceivable to have polls that expire before any vote was casted?
If so you might want to adjust a couple of things:

  1. remove this check
  1. add a check to avoiding dividing by 0 when there are no voterrs

At the top of this function I’d also add a require check for _pollId < pollLogs.length.

Minor: walletCredits is just the ether balance of the account, right? Naming the variable walletCredits confused me a little. I suggest you name this variable ethBalance or something.

1 Like

yeah…you got it right. Thanks for your feedback.
The points you’ve mentioned are valuable, I’ll work upon them. :slightly_smiling_face:

1 Like