Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slither: add slither analyzer to github workflow #12

Closed
wants to merge 13 commits into from
Closed

Conversation

roisindowling
Copy link
Contributor

No description provided.

@codecov-commenter
Copy link

codecov-commenter commented Jul 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.71%. Comparing base (3cf83ad) to head (3bf8b14).

Additional details and impacted files
@@           Coverage Diff            @@
##             main      #12    +/-   ##
========================================
  Coverage   98.71%   98.71%            
========================================
  Files          37       37            
  Lines        2326     2326            
  Branches      486      341   -145     
========================================
  Hits         2296     2296            
  Misses         29       29            
  Partials        1        1            

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

github-actions bot commented Jul 15, 2024

Slither report

THIS CHECKLIST IS NOT COMPLETE. Use --show-ignored-findings to show all the results.
Summary

arbitrary-send-eth

Impact: High
Confidence: Medium

function transferVET(address _to, uint256 _value) external onlyGovernanceWhenNotPaused nonReentrant {
TreasuryStorage storage $ = _getTreasuryStorage();
require($.transferLimitVET >= _value, "Treasury: transfer limit exceeded");
require(address(this).balance >= _value, "Treasury: insufficient VET balance");
(bool sent, ) = _to.call{ value: _value }("");
require(sent, "Failed to send VET");
}

function relay(address target, uint256 value, bytes calldata data) external payable virtual onlyGovernance {
(bool success, bytes memory returndata) = target.call{ value: value }(data);
Address.verifyCallResult(success, returndata);
}

divide-before-multiply

Impact: Medium
Confidence: Medium

function _rewardAmount(uint256 roundId, uint256 share) internal view returns (uint256) {
uint256 total = _emissionAmount(roundId);
uint256 variableAllocationPercentage = 100 - xAllocationVoting().getRoundBaseAllocationPercentage(roundId);
uint256 available = (total * variableAllocationPercentage) / 100;
uint256 rewardAmount = (available * share) / PERCENTAGE_PRECISION_SCALING_FACTOR;
return rewardAmount;
}

uninitialized-local

Impact: Medium
Confidence: Medium

uint256 unallocatedShare;

unused-return

Impact: Medium
Confidence: Medium

function start() external onlyRole(MINTER_ROLE) nonReentrant {
EmissionsStorage storage $ = _getEmissionsStorage();
require($.b3tr.paused() == false, "Emissions: B3TR token is paused");
require($.nextCycle == 1, "Emissions: Can only start emissions when next cycle = 1");
$.lastEmissionBlock = block.number;
$.xAllocationsGovernor.startNewRound();
$.nextCycle++;
}

function cancel(
GovernorStorageTypes.GovernorStorage storage self,
address account,
bool admin,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
if (account != proposalProposer(self, proposalId) && !admin) {
revert UnauthorizedAccess(account);
}
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.ALL_PROPOSAL_STATES_BITMAP ^
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Canceled) ^
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Executed)
);
if (account == proposalProposer(self, proposalId)) {
require(
GovernorStateLogic._state(self, proposalId) == GovernorTypes.ProposalState.Pending,
"Governor: proposal not pending"
);
}
bytes32 timelockId = self.timelockIds[proposalId];
if (timelockId != 0) {
// cancel
self.timelock.cancel(timelockId);
// cleanup
delete self.timelockIds[proposalId];
}
return _cancel(self, proposalId);
}

function _push(Checkpoints.Trace208 storage store, uint208 delta) private returns (uint208, uint208) {
return store.push(clock(), delta);
}

function _pushCheckpoint(Checkpoints.Trace208 storage store, uint208 delta) private returns (uint208, uint208) {
return store.push(clock(), delta);
}

function proposalVotes(
uint256 proposalId
) external view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) {
GovernorStorageTypes.GovernorStorage storage $ = getGovernorStorage();
return GovernorVotesLogic.getProposalVotes($, proposalId);
}

function distribute() external nonReentrant {
EmissionsStorage storage $ = _getEmissionsStorage();
require($.nextCycle > 1, "Emissions: Please start emissions first");
require(isNextCycleDistributable(), "Emissions: Next cycle not started yet");
// Mint emissions for current cycle
uint256 xAllocationsAmount = _calculateNextXAllocation();
uint256 vote2EarnAmount = _calculateVote2EarnAmount();
uint256 treasuryAmount = _calculateTreasuryAmount();
require(
xAllocationsAmount + vote2EarnAmount + treasuryAmount <= getRemainingEmissions(),
"Emissions: emissions would exceed B3TR supply cap"
);
$.lastEmissionBlock = block.number;
$.emissions[$.nextCycle] = Emission(xAllocationsAmount, vote2EarnAmount, treasuryAmount);
$.totalEmissions += xAllocationsAmount + vote2EarnAmount + treasuryAmount;
$.b3tr.mint($._xAllocations, xAllocationsAmount);
$.b3tr.mint($._vote2Earn, vote2EarnAmount);
$.b3tr.mint($._treasury, treasuryAmount);
$.xAllocationsGovernor.startNewRound();
emit EmissionDistributed($.nextCycle, xAllocationsAmount, vote2EarnAmount, treasuryAmount);
$.nextCycle++;
}

function withdraw(GovernorStorageTypes.GovernorStorage storage self, uint256 proposalId, address depositer) external {
uint256 amount = self.deposits[proposalId][depositer];
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.ALL_PROPOSAL_STATES_BITMAP ^
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Pending)
);
if (amount == 0) {
revert GovernorNoDepositToWithdraw(proposalId, depositer);
}
self.deposits[proposalId][depositer] = 0;
require(self.vot3.transfer(depositer, amount), "B3TRGovernor: transfer failed");
}

function deposit(GovernorStorageTypes.GovernorStorage storage self, uint256 amount, uint256 proposalId) external {
if (amount == 0) {
revert GovernorInvalidDepositAmount();
}
GovernorTypes.ProposalCore storage proposal = self.proposals[proposalId];
if (proposal.roundIdVoteStart == 0) {
revert GovernorNonexistentProposal(proposalId);
}
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Pending)
);
proposal.depositAmount += amount;
depositFunds(self, amount, msg.sender, proposalId);
}

function execute(
GovernorStorageTypes.GovernorStorage storage self,
address contractAddress, // Address of the calling contract
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Succeeded) |
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Queued)
);
// mark as executed before calls to avoid reentrancy
self.proposals[proposalId].executed = true;
// before execute: register governance call in queue.
if (GovernorGovernanceLogic.executor(self) != contractAddress) {
for (uint256 i; i < targets.length; ++i) {
if (targets[i] == address(this)) {
self.governanceCall.pushBack(keccak256(calldatas[i]));
}
}
}
_executeOperations(self, contractAddress, proposalId, targets, values, calldatas, descriptionHash);
// after execute: cleanup governance call queue.
if (GovernorGovernanceLogic.executor(self) != contractAddress && !self.governanceCall.empty()) {
self.governanceCall.clear();
}
emit ProposalExecuted(proposalId);
return proposalId;
}

function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual {
uint256 denominator = quorumDenominator();
if (newQuorumNumerator > denominator) {
revert GovernorInvalidQuorumFraction(newQuorumNumerator, denominator);
}
uint256 oldQuorumNumerator = quorumNumerator();
VotesQuorumFractionStorage storage $ = _getVotesQuorumFractionStorage();
$._quorumNumeratorHistory.push(clock(), SafeCast.toUint208(newQuorumNumerator));
emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator);
}

function relay(address target, uint256 value, bytes calldata data) external payable virtual onlyGovernance {
(bool success, bytes memory returndata) = target.call{ value: value }(data);
Address.verifyCallResult(success, returndata);
}

function castVote(
GovernorStorageTypes.GovernorStorage storage self,
uint256 proposalId,
address voter,
uint8 support,
string calldata reason
) external returns (uint256) {
GovernorStateLogic.validateStateBitmap(self, proposalId, GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Active));
uint256 weight = self.vot3.getPastVotes(voter, GovernorProposalLogic._proposalSnapshot(self, proposalId));
uint256 power = Math.sqrt(weight) * 1e9;
if (weight < GovernorConfigurator.getVotingThreshold(self)) {
revert GovernorVotingThresholdNotMet(weight, GovernorConfigurator.getVotingThreshold(self));
}
_countVote(self, proposalId, voter, support, weight, power);
self.voterRewards.registerVote(GovernorProposalLogic._proposalSnapshot(self, proposalId), voter, weight, Math.sqrt(weight));
emit VoteCast(voter, proposalId, support, weight, power, reason);
return weight;
}

function queue(
GovernorStorageTypes.GovernorStorage storage self,
address contractAddress, // Address of the calling contract
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Succeeded)
);
uint48 etaSeconds = _queueOperations(
self,
contractAddress,
proposalId,
targets,
values,
calldatas,
descriptionHash
);
if (etaSeconds != 0) {
self.proposals[proposalId].etaSeconds = etaSeconds;
emit ProposalQueued(proposalId, etaSeconds);
} else {
revert GovernorQueueNotImplemented();
}
return proposalId;
}

function updateQuorumNumerator(
GovernorStorageTypes.GovernorStorage storage self,
uint256 newQuorumNumerator
) external {
uint256 denominator = quorumDenominator();
uint256 oldQuorumNumerator = quorumNumerator(self);
if (newQuorumNumerator > denominator) {
revert GovernorInvalidQuorumFraction(newQuorumNumerator, denominator);
}
self.quorumNumeratorHistory.push(GovernorClockLogic.clock(self), SafeCast.toUint208(newQuorumNumerator));
emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator);
}

shadowing-local

Impact: Low
Confidence: High

function addApp(address teamWalletAddress, address admin, string memory appName, string memory metadataURI) external;

function _updateAppMetadata(bytes32 appId, string memory metadataURI) internal virtual;

missing-zero-check

Impact: Low
Confidence: Medium

function transferVET(address _to, uint256 _value) external onlyGovernanceWhenNotPaused nonReentrant {

function relay(address target, uint256 value, bytes calldata data) external payable virtual onlyGovernance {

calls-loop

Impact: Low
Confidence: Medium

function getAppsOfRound(
uint256 roundId
) external view returns (X2EarnAppsDataTypes.AppWithDetailsReturnType[] memory) {
RoundsStorageStorage storage $ = _getRoundsStorageStorage();
bytes32[] memory appsInRound = $._appsEligibleForVoting[roundId];
uint256 length = appsInRound.length;
X2EarnAppsDataTypes.AppWithDetailsReturnType[] memory allApps = new X2EarnAppsDataTypes.AppWithDetailsReturnType[](
length
);
for (uint i; i < length; i++) {
allApps[i] = x2EarnApps().app(appsInRound[i]);
}
return allApps;
}

reentrancy-events

Impact: Low
Confidence: Medium

function cancel(
GovernorStorageTypes.GovernorStorage storage self,
address account,
bool admin,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
if (account != proposalProposer(self, proposalId) && !admin) {
revert UnauthorizedAccess(account);
}
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.ALL_PROPOSAL_STATES_BITMAP ^
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Canceled) ^
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Executed)
);
if (account == proposalProposer(self, proposalId)) {
require(
GovernorStateLogic._state(self, proposalId) == GovernorTypes.ProposalState.Pending,
"Governor: proposal not pending"
);
}
bytes32 timelockId = self.timelockIds[proposalId];
if (timelockId != 0) {
// cancel
self.timelock.cancel(timelockId);
// cleanup
delete self.timelockIds[proposalId];
}
return _cancel(self, proposalId);
}

function queue(
GovernorStorageTypes.GovernorStorage storage self,
address contractAddress, // Address of the calling contract
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Succeeded)
);
uint48 etaSeconds = _queueOperations(
self,
contractAddress,
proposalId,
targets,
values,
calldatas,
descriptionHash
);
if (etaSeconds != 0) {
self.proposals[proposalId].etaSeconds = etaSeconds;
emit ProposalQueued(proposalId, etaSeconds);
} else {
revert GovernorQueueNotImplemented();
}
return proposalId;
}

function execute(
GovernorStorageTypes.GovernorStorage storage self,
address contractAddress, // Address of the calling contract
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Succeeded) |
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Queued)
);
// mark as executed before calls to avoid reentrancy
self.proposals[proposalId].executed = true;
// before execute: register governance call in queue.
if (GovernorGovernanceLogic.executor(self) != contractAddress) {
for (uint256 i; i < targets.length; ++i) {
if (targets[i] == address(this)) {
self.governanceCall.pushBack(keccak256(calldatas[i]));
}
}
}
_executeOperations(self, contractAddress, proposalId, targets, values, calldatas, descriptionHash);
// after execute: cleanup governance call queue.
if (GovernorGovernanceLogic.executor(self) != contractAddress && !self.governanceCall.empty()) {
self.governanceCall.clear();
}
emit ProposalExecuted(proposalId);
return proposalId;
}

function deposit(uint256 amount, bytes32 appId) external returns (bool) {
X2EarnRewardsPoolStorage storage $ = _getX2EarnRewardsPoolStorage();
// check that app exists
require($.x2EarnApps.appExists(appId), "X2EarnRewardsPool: app does not exist");
// increase available amount for the app
$.availableFunds[appId] += amount;
// transfer tokens to this contract
require($.b3tr.transferFrom(msg.sender, address(this), amount), "X2EarnRewardsPool: deposit transfer failed");
emit NewDeposit(amount, appId, msg.sender);
return true;
}

function depositFunds(
GovernorStorageTypes.GovernorStorage storage self,
uint256 amount,
address depositor,
uint256 proposalId
) internal {
require(self.vot3.transferFrom(depositor, address(this), amount), "B3TRGovernor: transfer failed");
self.deposits[proposalId][depositor] += amount;
emit ProposalDeposit(depositor, proposalId, amount);
}

function _propose(
GovernorStorageTypes.GovernorStorage storage self,
address proposer,
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description,
uint256 startRoundId,
uint256 depositAmount
) private returns (uint256) {
uint256 depositThresholdAmount = GovernorDepositLogic._depositThreshold(self);
_setProposal(
self,
proposalId,
proposer,
SafeCast.toUint32(self.xAllocationVoting.votingPeriod()),
startRoundId,
targets.length > 0,
depositAmount,
depositThresholdAmount
);
if (depositAmount > 0) {
GovernorDepositLogic.depositFunds(self, depositAmount, proposer, proposalId);
}
emit ProposalCreated(
proposalId,
proposer,
targets,
values,
new string[](targets.length),
calldatas,
description,
startRoundId,
depositThresholdAmount
);
return proposalId;
}

function castVote(
GovernorStorageTypes.GovernorStorage storage self,
uint256 proposalId,
address voter,
uint8 support,
string calldata reason
) external returns (uint256) {
GovernorStateLogic.validateStateBitmap(self, proposalId, GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Active));
uint256 weight = self.vot3.getPastVotes(voter, GovernorProposalLogic._proposalSnapshot(self, proposalId));
uint256 power = Math.sqrt(weight) * 1e9;
if (weight < GovernorConfigurator.getVotingThreshold(self)) {
revert GovernorVotingThresholdNotMet(weight, GovernorConfigurator.getVotingThreshold(self));
}
_countVote(self, proposalId, voter, support, weight, power);
self.voterRewards.registerVote(GovernorProposalLogic._proposalSnapshot(self, proposalId), voter, weight, Math.sqrt(weight));
emit VoteCast(voter, proposalId, support, weight, power, reason);
return weight;
}

function _countVote(
uint256 roundId,
address voter,
bytes32[] memory apps,
uint256[] memory weights
) internal virtual override {
if (hasVoted(roundId, voter)) {
revert GovernorAlreadyCastVote(voter);
}
RoundVotesCountingStorage storage $ = _getRoundVotesCountingStorage();
// Get the start of the round
uint256 roundStart = roundSnapshot(roundId);
// To hold the total weight of votes cast by the voter
uint256 totalWeight;
// To hold the total adjustment to the quadratic funding value for the given app
uint256 totalQFVotesAdjustment;
// Get the total voting power of the voter to use in the for loop to check
// if the total weight of votes cast by the voter is greater than the voter's available voting power
uint256 voterAvailableVotes = getVotes(voter, roundStart);
// Iterate through the apps and weights to calculate the total weight of votes cast by the voter
for (uint256 i; i < apps.length; i++) {
// Update the total weight of votes cast by the voter
totalWeight += weights[i];
if (totalWeight > voterAvailableVotes) {
revert GovernorInsufficientVotingPower();
}
// Check if the app is eligible for votes in the current round
if (!isEligibleForVote(apps[i], roundId)) {
revert GovernorAppNotAvailableForVoting(apps[i]);
}
// Get the current sum of the square roots of individual votes for the given project
uint256 qfAppVotesPreVote = $._roundVotes[roundId].votesReceivedQF[apps[i]]; // ∑(sqrt(votes)) -> sqrt(votes1) + sqrt(votes2) + ... + sqrt(votesN)
// Calculate the new sum of the square roots of individual votes for the given project
uint256 newQFVotes = Math.sqrt(weights[i]); // sqrt(votes)
uint256 qfAppVotesPostVote = qfAppVotesPreVote + newQFVotes; // ∑(sqrt(votes)) -> sqrt(votes1) + sqrt(votes2) + ... + sqrt(votesN) + sqrt(votesN+1)
// Calculate the adjustment to the quadratic funding value for the given app
totalQFVotesAdjustment += (qfAppVotesPostVote * qfAppVotesPostVote) - (qfAppVotesPreVote * qfAppVotesPreVote); // (sqrt(votes1) + ... + sqrt(votesN+1))^2 - (sqrt(votes1) + ... + sqrt(votesN))^2
// Update the quadratic funding votes received for the given app - sum of the square roots of individual votes
$._roundVotes[roundId].votesReceivedQF[apps[i]] = qfAppVotesPostVote; // ∑(sqrt(votes)) -> sqrt(votes1) + sqrt(votes2) + ... + sqrt(votesN+1)
$._roundVotes[roundId].votesReceived[apps[i]] += weights[i]; // ∑votes + votesN+1
}
// Check if the total weight of votes cast by the voter is greater than the voting threshold
if (totalWeight < votingThreshold()) {
revert GovernorVotingThresholdNotMet(votingThreshold(), totalWeight);
}
// Apply the total adjustment to storage
$._roundVotes[roundId].totalVotesQF += totalQFVotesAdjustment; // update the total quadratic funding value for the round - ∑(∑sqrt(votes))^2 -> (sqrt(votesAppX1) + sqrt(votesAppX2) + ...)^2 + (sqrt(votesAppY1) + sqrt(votesAppY2) + ...)^2 + ...
$._roundVotes[roundId].totalVotes += totalWeight; // update total votes -> ∑votes + votesN+1
$._roundVotes[roundId].hasVoted[voter] = true; // mark the voter as having voted
$._roundVotes[roundId].totalVoters++; // increment the total number of voters
// save that user cast vote only the first time
if (!$._hasVotedOnce[voter]) {
$._hasVotedOnce[voter] = true;
}
// Register the vote for rewards calculation where the vote power is the square root of the total votes cast by the voter
voterRewards().registerVote(roundStart, voter, totalWeight, Math.sqrt(totalWeight));
// Emit the AllocationVoteCast event
emit AllocationVoteCast(voter, roundId, apps, weights);
}

timestamp

Impact: Low
Confidence: Medium

function queue(
GovernorStorageTypes.GovernorStorage storage self,
address contractAddress, // Address of the calling contract
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
GovernorStateLogic.validateStateBitmap(
self,
proposalId,
GovernorStateLogic.encodeStateBitmap(GovernorTypes.ProposalState.Succeeded)
);
uint48 etaSeconds = _queueOperations(
self,
contractAddress,
proposalId,
targets,
values,
calldatas,
descriptionHash
);
if (etaSeconds != 0) {
self.proposals[proposalId].etaSeconds = etaSeconds;
emit ProposalQueued(proposalId, etaSeconds);
} else {
revert GovernorQueueNotImplemented();
}
return proposalId;
}

@roisindowling roisindowling marked this pull request as ready for review July 15, 2024 13:15
@roisindowling roisindowling requested a review from a team as a code owner July 15, 2024 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants