0
pragma solidity ^0.4.23;

contract Voting {
    bytes32[] public candidateList;

    uint public totalTokens;
    uint public balanceTokens;
    uint public tokenPrice;

    // what is the voter address?
    // total tokens purchased
    // tokens voted per candidate 

    struct voter {
        address voterAddress;
        uint tokensBought;
        uint[] tokensUsedPerCandidate;
    }

    mapping(address => voter) public voterInfo;
    mapping(bytes32 => uint) public votesReceived;

    constructor (uint _totalTokens, uint _tokenPrice, bytes32[] _candidateNames) public {
        totalTokens = _totalTokens;
        balanceTokens = _totalTokens;
        tokenPrice = _tokenPrice;
        candidateList = _candidateNames;
    }

    //1. Users should be able to purchase tokens 
    //2. Users should be able to vote for candidates with tokens
    //3. Anyone should have the ability to lookup voter info

    function buy() payable public {
        uint tokensToBuy = msg.value / tokenPrice;
        require(tokensToBuy <= balanceTokens);
        voterInfo[msg.sender].voterAddress = msg.sender;
        voterInfo[msg.sender].tokensBought += tokensToBuy;
        balanceTokens -= tokensToBuy;
    }

    function voteForCandidate(bytes32 candidate, uint tokens) public {
        // Check to make sure user has enough tokens to vote
        // Increment vote count for candidate
        // Update the voter struct tokensUsedPerCandidate for this voter

        uint availableTokens = voterInfo[msg.sender].tokensBought - totalTokensUsed(voterInfo[msg.sender].tokensUsedPerCandidate);

        require(tokens <= availableTokens, "You don't have enough tokens");
        votesReceived[candidate] += tokens;

        if (voterInfo[msg.sender].tokensUsedPerCandidate.length == 0) {
            for(uint i=0; i<candidateList.length; i++) {
                voterInfo[msg.sender].tokensUsedPerCandidate.push(0);
            }
        }

        uint index = indexOfCandidate(candidate);
        voterInfo[msg.sender].tokensUsedPerCandidate[index] += tokens;
    }

    function indexOfCandidate(bytes32 candidate) view public returns(uint) {
        for(uint i=0; i<candidateList.length; i++) {
            if (candidateList[i] == candidate) {
                return i;
            }
        }
        return uint(-1);
    }

    function totalTokensUsed(uint[] _tokensUsedPerCandidate) private pure returns (uint) {
        uint totalUsedTokens = 0;
        for(uint i=0; i<_tokensUsedPerCandidate.length; i++) {
            totalUsedTokens += _tokensUsedPerCandidate[i];
        }
        return totalUsedTokens;
    }

    function voterDetails(address user) view public returns (uint, uint[]) {
        return (voterInfo[user].tokensBought, voterInfo[user].tokensUsedPerCandidate);
    }

    function tokensSold() public view returns (uint) {
        return totalTokens - balanceTokens;
    }

    function allCandidates() public view returns (bytes32[]) {
        return candidateList;
    }

    function totalVotesFor(bytes32 candidate) public view returns (uint) {
        return votesReceived[candidate];
   }
}
1

The function to transfer Ether to an account could be something like this:

function transferBalance() public {
    owner.transfer(address(this).balance);
}

And in your constructor, you can set owner like this (whoever deployed the contract is the owner and only they get the Ether)

 owner = msg.sender
1
  • This implementation allows anyone to initiate transfer. This does not mean that ether might be stolen, because it is anyway sent to the owner, but this could make it hard to track how much ether was withdrawn. – Mikhail Vladimirov Mar 26 '19 at 6:34
0

[I edited the original question to make the code more easily understandable. Once I did that, it became obvious that] this code (as written) contains no way to extract any ether that may be accumulated via the payable buy function.

The only "sort of" explanation for why the code may look like this is that writer of the contract is supposed to be inherited by another contract that provides the code to extract the ether.

Separating the logic that accepts the ether from the logic that allows the ether to be removed from the contract is, I think, a very bad design, as is incorporating the voting function into the contact that accepts the ether.

Either, there should be two contracts (one that deals with the ether and one that deals with the voting) or a single contract dealing fully with both.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.