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.