Executing Proposal when Majority votes against

Hi everyone! I'm having an issue with governance contracts.

I want to build functionality such as a proposal has 2 calldatas and I want to execute one of them based on the results of the voting. The first should be executed when the majority votes FOR. The second one is to be executed when the majority votes AGAINST. Is it possible to do this? If so, how can I achieve this?

2 Issues I am facing:

  1. When proposal is created, it creates a proposal ID using 2 calldatas i.e the parameters (targets, values, calldatas, reason) and when I am trying to execute only one calldata using if condition, it is saying proposal ID doesn't exist.
  2. It only executes when the vote has succeeded i.e VotesFor > VotesAgainst and Quorum Reached. I am not able to execute when VotesAgainst > VotesFor as the proposal is not "Succeeded" State when this happens. It goes into "Defeated" state.

The way I have approached this is to store a state in the governor contract based on the voting when the voting period ends, and handle the executing in my "BOX" contract. (Contract owned by the DAO).

If there a better way to do this?

Thanks, in advance!

Hi, sorry for the delay. Have you made progress on this issue?

Can you share the code you have written?

I wanted to build a functionality where I am calling GPT through Chainlink Functions to vote on a proposal with 30% voting power each time. The issue was that GPT doesn't have any tokens delegated, so I changed the logic in the "GovernorCountingSimple" contract to account for GPT vote, specifically changed the logic in the "voteSucceeded" as the proposal is only executed when it is succeeded.

function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalData storage _proposal = proposalIdToData[proposalId];
        if(_proposal.shouldGPTVote == true) {
            return proposalIdToGPTData[proposalId].hasVoted; //this is made true when Chainlink DON calls fulfill request
        } else {
            (
            uint256 againstVotes,
            uint256 forVotes,
        ) = proposalVotes(proposalId);
            return forVotes > againstVotes;
        }
    }

When chainlink node calls fulfillRequest with GPTs vote, I am storing it in a mapping and then calling my custom execute function which calls the Time-locks execute functions.

        (
            uint256 againstVotes,
            uint256 forVotes,
        ) = proposalVotes(_proposalId);

        uint8 result = forVotes > againstVotes ? 1 : 0;
        uint8 gptVote = proposalIdToGPTData[_proposalId]._voteWay;

        uint newresult = Decide.whichOne(gptVote, result, againstVotes, forVotes); //custom library which is re-counting the votes based on GPT vote

        ProposalData memory data = proposalIdToData[_proposalId];
        address[] memory targets = new address[](1);
        targets[0] = data.targets[0];

        uint256[] memory values = new uint256[](1);
        values[0] = 0;

        bytes[] memory datas = new bytes[](1);
        datas[0] = data.calldatas[newresult]; //only passing 1 calldata

        proposalIdToData[_proposalId].result = newresult;

        super._execute(_proposalId, targets, values, datas, "reason");

The library looks like this:

library Decide {
    
    function whichOne(uint8 way, uint8 result, uint256 againstVotes, uint256 forVotes) internal view returns (uint8) {
        if (result == 1) {
            //majority voted FOR
            if (way == 1) {
                return 1;
            } else {
                //recount
                if (
                    (againstVotes + calculateGPTVotingPower(againstVotes, forVotes)) >
                    forVotes
                ) {
                    return 0;
                } else {
                    return 1;
                }
                
            }
        } else {
            if(way == 1) {
                if (
                    (forVotes + calculateGPTVotingPower(againstVotes, forVotes)) >
                    againstVotes
                ) {
                    return 1;
                } else {
                    return 0;
                }
            } else {
                return 0;
            }
        }
    }

    function calculateGPTVotingPower(uint256 againstVotes, uint256 forVotes
    ) public view returns (uint256) {
        
        uint256 bps = 3000; // 30%
        uint256 _30percent = calculatePercentage(forVotes + againstVotes, bps);

        return _30percent;
    }

    function calculatePercentage(
        uint256 amount,
        uint256 bps
    ) public pure returns (uint256) {
        return (amount * bps) / 10_000;
    }
}

I noticed that there is a vulnerability fix made in the Execute Batch functions in TimeLock which was causing the proposal ID issue (TimelockController Vulnerability Post-mortem).

I changed the reentrancy logic to account for GPT and it seems to work fine but I would love your suggestions on this.