1

I am receiving the following message:

"Warning! Error encountered during contract execution [execution reverted]"

What I'm trying to do is simple, I have a contract, where I don't get token, I just use ethereum and users just pay gas fee to miners.

What I'm trying to do here is call a contract function, but it adds data to the blockchain, which wastes gas.

Here's the contract:

https://goerli.etherscan.io/address/0xa46dc78595315b94904182013888adb439c6009f

Contract Code:

/**
 * @title Ballot
 * @dev Implements voting process along with vote delegation
 */
contract Ballot is IHelper, Guard, Logging {
    Confirmed[6] private _confirmedVotes;
    Abstention private _abstentionVotes;
    uint[6] private _candidates;
    uint private _totalConfirmedVotes;
    mapping(address => bool) private _electorHasAlreadyVoted;

    constructor() {
        _registerCandidates();

        emit LogStartElection("Beginning of the election period, ballot box released!", getResult());
    }

    /** @dev Register Candidates **/
    function _registerCandidates() private {
        for (uint i = 0; i < _candidates.length; i++) {
            _candidates[i] = i;
            _confirmedVotes[i].candidate = i;
        }
    }

    /**
     * @dev Return Elector Has Already Voted
     * @return value of '_electorHasAlreadyVoted'
     */
    function getElectorHasAlreadyVoted() public view returns (bool) {
        return _electorHasAlreadyVoted[msg.sender];
    }

    /**
     * @dev Return Electoral Result
     * @return value of 'ElectionResult'
     */
    function getResult() public view returns (ElectionResult memory) {
        return
            ElectionResult({
                candidates: _candidates,
                totalConfirmedVotes: _totalConfirmedVotes,
                confirmedVotes: _confirmedVotes,
                abstentionVotes: _abstentionVotes
            });
    }

    /**
     * @dev Elector Vote Confirmation
     * @param candidateID value to store
     */
    function confirmVote(
        uint candidateID
    ) public onlyCandidateRegistered(candidateID, _candidates.length) onlyElectorWhoDidNotVote(_electorHasAlreadyVoted[msg.sender]) {
        _confirmedVotes[candidateID].totalVotes++;
        _confirmedVotes[candidateID].elector.push(msg.sender);
        _confirmedVotes[candidateID].candidate = candidateID;

        _electorHasAlreadyVoted[msg.sender] = true;

        _totalConfirmedVotes++;

        emit LogElectorVote("Vote Confirmed and Computed!", getResult());
    }

    /** @dev Elector Vote Abstention **/
    function abstainVote() public onlyElectorWhoDidNotVote(_electorHasAlreadyVoted[msg.sender]) {
        _abstentionVotes.totalVotes++;
        _abstentionVotes.elector.push(msg.sender);

        _electorHasAlreadyVoted[msg.sender] = true;

        emit LogElectorVote("Vote Abstained and Computed!", getResult());
    }
}

DApp Ethers code:

    const PROVIDER = ethers.providers.InfuraProvider.getWebSocketProvider({ chainId: 5, name: 'goerli' });
    const PRIVATE_WALLET = new ethers.Wallet(process.env.REACT_APP_WALLET_PRIVATE_KEY || '', PROVIDER);
    const CONTRACT = new ethers.Contract(
                    process.env.REACT_APP_SOLIDITY_CONTRACT_ADDRESS || '',
                    BallotContract.abi,
                    PRIVATE_WALLET
                )
    
    const ELECTOR_SIGNER = CONTRACT.connect(ELECTOR_WALLET);
    const CONTRACT_ADDRESS = await infura.contract.resolvedAddress;
    const GAS_LIMIT = await ELECTOR_SIGNER.estimateGas.confirmVote(1);
    const TRANSACTION_HASH = await window.ethereum?.request({
                                method: 'eth_sendTransaction',
                                params: [
                                    {
                                        from: ELECTOR_SIGNER,
                                        to: CONTRACT_ADDRESS,
                                        gas: estimateGas._hex,
                                        value: 0,
                                    },
                                ],
                            })
const TRANSACTION_RECEIPT = await PROVIDER.waitForTransaction(TRANSACTION_HASH);
const RESPONSE = await ELECTOR_SIGNER.confirmVote(1);

enter image description here

THIAGO DE BONIS
  • 760
  • 7
  • 22

1 Answers1

1

For a contract to accept ETH (or generally native tokens - BNB on Binance Smart Chain, MATIC on Polygon, ...), it needs to implement either of the special functions - receive() or payable fallback(). If the contract doesn't implement any of these functions, it rejects the incoming transfer.

contract Ballot {
    // ...

    receive() external payable {
        // can be empty; or you can redirect the incoming transfer to an EOA
    }
}

When you just send empty value and empty data, it tries to invoke the fallback() function, that is also not present. So the solution is similar in this case - implement the fallback() special function.

contract Ballot {
    // ...

    fallback() external payable {
        // can be empty
    }
}
Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • The contract does not actually receive any ETH, users just pay gas fee to miners. Is it still mandatory? Besides that, I had already tested the contract and it works. The problem I'm having is when I connect() on Metamask's Wallet it brings me transaction status 0. – THIAGO DE BONIS Dec 19 '22 at 23:12
  • @THIAGODEBONIS Thanks for the clarification, I missed the 0 value. Appended my answer: Since both the `value` and `data` fields are empty, then it's trying to execute the `fallback()` function. And since this function is not defined in your contract, the contract rejects the transaction. – Petr Hejda Dec 19 '22 at 23:16
  • Testing wait please... – THIAGO DE BONIS Dec 19 '22 at 23:29
  • Alright, now he really is successfully mining the block. However, after waiting for ```waitForTransaction``` and giving ```status: 1```, I ran the contract function with the user's wallet connected like this: ```CONST WALLET_CONNECTED = PROVIDER_CONTRACT.connect('0xe9ac4 ')``` and then I did: ```const RESPONSE = await ẀALLET_CONNECTED.confirmVote(3)``` and it returned the following error: ```"Error: VoidSigner cannot sign transactions"``` – THIAGO DE BONIS Dec 19 '22 at 23:45
  • @THIAGODEBONIS Awesome. :) ... The `VoidSigner` is a different issue, related to your JS code, not to the Solidity code. It seems related to using private key directly with `Wallet` instance - in combination with `ethereum.request` which should request MetaMask (or other browser wallet) to sign the transaction. Usual way is to chose just one approach - sign with MetaMask OR with the private key directly. – Petr Hejda Dec 19 '22 at 23:52
  • I need these two approaches because of the following. 1) The getResult() function is called with the user logged in or not with the wallet, so I use the private key in the provider to call it. 2) I need the user's Wallet to call the confirmVote() function, as his wallet hash will be stored on the blockchain and it will be checked if he has already voted. Am I doing it wrong? – THIAGO DE BONIS Dec 19 '22 at 23:58