Skip to content

Commit c22812d

Browse files
MikeHathawayMike
andauthored
Safe casting (#50)
* use safeCast for integer casting * fix overflow issue * remove unnecessary using keyword; get contract size under limit * pr feedback --------- Co-authored-by: Mike <[email protected]>
1 parent 43e8324 commit c22812d

File tree

4 files changed

+36
-23
lines changed

4 files changed

+36
-23
lines changed

src/grants/GrantFund.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
pragma solidity 0.8.16;
44

5-
import { Governor } from "@oz/governance/Governor.sol";
6-
import { IGovernor } from "@oz/governance/IGovernor.sol";
7-
import { IVotes } from "@oz/governance/utils/IVotes.sol";
5+
import { Governor } from "@oz/governance/Governor.sol";
6+
import { IGovernor } from "@oz/governance/IGovernor.sol";
7+
import { IVotes } from "@oz/governance/utils/IVotes.sol";
8+
import { SafeCast } from "@oz/utils/math/SafeCast.sol";
89

910
import { Maths } from "./libraries/Maths.sol";
1011

@@ -124,7 +125,7 @@ contract GrantFund is IGrantFund, ExtraordinaryFunding, StandardFunding {
124125
// set initial voting power and remaining voting power
125126
if (voter.votingPower == 0) {
126127

127-
uint128 newVotingPower = uint128(_getFundingStageVotingPower(msg.sender, screeningStageEndBlock));
128+
uint128 newVotingPower = SafeCast.toUint128(_getFundingStageVotingPower(msg.sender, screeningStageEndBlock));
128129

129130
voter.votingPower = newVotingPower;
130131
voter.remainingVotingPower = newVotingPower;
@@ -239,7 +240,7 @@ contract GrantFund is IGrantFund, ExtraordinaryFunding, StandardFunding {
239240
// set initial voting power and remaining voting power
240241
if (voter.votingPower == 0) {
241242

242-
uint128 newVotingPower = uint128(_getFundingStageVotingPower(msg.sender, screeningStageEndBlock));
243+
uint128 newVotingPower = SafeCast.toUint128(_getFundingStageVotingPower(msg.sender, screeningStageEndBlock));
243244

244245
voter.votingPower = newVotingPower;
245246
voter.remainingVotingPower = newVotingPower;

src/grants/base/ExtraordinaryFunding.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
pragma solidity 0.8.16;
44

5-
import { IERC20 } from "@oz/token/ERC20/IERC20.sol";
5+
import { IERC20 } from "@oz/token/ERC20/IERC20.sol";
6+
import { SafeCast } from "@oz/utils/math/SafeCast.sol";
67

78
import { Funding } from "./Funding.sol";
89

@@ -47,6 +48,7 @@ abstract contract ExtraordinaryFunding is Funding, IExtraordinaryFunding {
4748

4849
ExtraordinaryFundingProposal storage proposal = extraordinaryFundingProposals[proposalId_];
4950

51+
// since we are casting from uint128 to uint256, we can safely assume that the value will not overflow
5052
uint256 tokensRequested = uint256(proposal.tokensRequested);
5153

5254
// revert if executed or if the proposal has received more votes than minimumThreshold and tokensRequestedPercentage of all tokens
@@ -95,8 +97,8 @@ abstract contract ExtraordinaryFunding is Funding, IExtraordinaryFunding {
9597

9698
// store newly created proposal
9799
newProposal.proposalId = proposalId_;
98-
newProposal.startBlock = uint128(block.number);
99-
newProposal.endBlock = uint128(endBlock_);
100+
newProposal.startBlock = SafeCast.toUint128(block.number);
101+
newProposal.endBlock = SafeCast.toUint128(endBlock_);
100102
newProposal.tokensRequested = totalTokensRequested;
101103

102104
emit ProposalCreated(
@@ -137,7 +139,7 @@ abstract contract ExtraordinaryFunding is Funding, IExtraordinaryFunding {
137139

138140
// check voting power at snapshot block
139141
votes_ = _getVotes(account_, block.number, abi.encode(proposalId_));
140-
proposal.votesReceived += uint112(votes_);
142+
proposal.votesReceived += SafeCast.toUint112(votes_);
141143

142144
// record that voter has voted on this extraorindary funding proposal
143145
hasVotedExtraordinary[proposalId_][account_] = true;

src/grants/base/Funding.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pragma solidity 0.8.16;
44

55
import { Governor } from "@oz/governance/Governor.sol";
66
import { ReentrancyGuard } from "@oz/security/ReentrancyGuard.sol";
7+
import { SafeCast } from "@oz/utils/math/SafeCast.sol";
78

89
abstract contract Funding is Governor, ReentrancyGuard {
910

@@ -118,7 +119,7 @@ abstract contract Funding is Governor, ReentrancyGuard {
118119
}
119120

120121
// update tokens requested for additional calldata
121-
tokensRequested_ += uint128(tokensRequested);
122+
tokensRequested_ += SafeCast.toUint128(tokensRequested);
122123

123124
unchecked { ++i; }
124125
}

src/grants/base/StandardFunding.sol

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
pragma solidity 0.8.16;
44

5-
import { IERC20 } from "@oz/token/ERC20/IERC20.sol";
6-
import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";
5+
import { IERC20 } from "@oz/token/ERC20/IERC20.sol";
6+
import { SafeCast } from "@oz/utils/math/SafeCast.sol";
7+
import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";
78

89
import { Funding } from "./Funding.sol";
910

@@ -128,7 +129,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
128129
}
129130

130131
// set the distribution period to start at the current block
131-
uint48 startBlock = uint48(block.number);
132+
uint48 startBlock = SafeCast.toUint48(block.number);
132133
uint48 endBlock = startBlock + DISTRIBUTION_PERIOD_LENGTH;
133134

134135
// set new value for currentDistributionId
@@ -140,7 +141,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
140141
newDistributionPeriod.startBlock = startBlock;
141142
newDistributionPeriod.endBlock = endBlock;
142143
uint256 gbc = Maths.wmul(treasury, GLOBAL_BUDGET_CONSTRAINT);
143-
newDistributionPeriod.fundsAvailable = uint128(gbc);
144+
newDistributionPeriod.fundsAvailable = SafeCast.toUint128(gbc);
144145

145146
// decrease the treasury by the amount that is held for allocation in the new distribution period
146147
treasury -= gbc;
@@ -180,6 +181,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
180181

181182
/**
182183
* @notice Updates Treasury with surplus funds from distribution.
184+
* @dev Counters incremented in an unchecked block due to being bounded by array length of at most 10.
183185
* @param distributionId_ distribution Id of updating distribution
184186
*/
185187
function _updateTreasury(
@@ -319,7 +321,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
319321
if (proposal.fundingVotesReceived < 0) return false;
320322

321323
// update counters
322-
sum += uint128(proposal.fundingVotesReceived);
324+
sum += uint128(proposal.fundingVotesReceived); // since we are converting from int128 to uint128, we can safely assume that the value will not overflow
323325
totalTokensRequested += proposal.tokensRequested;
324326

325327
// check if slate of proposals exceeded budget constraint ( 90% of GBC )
@@ -452,14 +454,15 @@ abstract contract StandardFunding is Funding, IStandardFunding {
452454
/**
453455
* @notice Calculates the sum of funding votes allocated to a list of proposals.
454456
* @dev Only iterates through a maximum of 10 proposals that made it through the screening round.
455-
* @dev Counters incremented in an unchecked block due to being bounded by array length.
457+
* @dev Counters incremented in an unchecked block due to being bounded by array length of at most 10.
456458
* @param proposalIdSubset_ Array of proposal Ids to sum.
457459
* @return sum_ The sum of the funding votes across the given proposals.
458460
*/
459461
function _sumProposalFundingVotes(
460462
uint256[] memory proposalIdSubset_
461463
) internal view returns (uint128 sum_) {
462464
for (uint i = 0; i < proposalIdSubset_.length;) {
465+
// since we are converting from int128 to uint128, we can safely assume that the value will not overflow
463466
sum_ += uint128(standardFundingProposals[proposalIdSubset_[i]].fundingVotesReceived);
464467

465468
unchecked { ++i; }
@@ -505,6 +508,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
505508

506509
// voter had already cast a funding vote on this proposal
507510
if (voteCastIndex != -1) {
511+
// since we are converting from int256 to uint256, we can safely assume that the value will not overflow
508512
FundingVoteParams storage existingVote = votesCast[uint256(voteCastIndex)];
509513

510514
// can't change the direction of a previous vote
@@ -524,7 +528,10 @@ abstract contract StandardFunding is Funding, IStandardFunding {
524528
}
525529

526530
// calculate the cumulative cost of all votes made by the voter
527-
uint128 cumulativeVotePowerUsed = uint128(_sumSquareOfVotesCast(votesCast));
531+
// and check that attempted votes cast doesn't overflow uint128
532+
uint256 sumOfTheSquareOfVotesCast = _sumSquareOfVotesCast(votesCast);
533+
if (sumOfTheSquareOfVotesCast > type(uint128).max) revert InsufficientVotingPower();
534+
uint128 cumulativeVotePowerUsed = SafeCast.toUint128(sumOfTheSquareOfVotesCast);
528535

529536
// check that the voter has enough voting power remaining to cast the vote
530537
if (cumulativeVotePowerUsed > votingPower) revert InsufficientVotingPower();
@@ -533,17 +540,17 @@ abstract contract StandardFunding is Funding, IStandardFunding {
533540
voter_.remainingVotingPower = votingPower - cumulativeVotePowerUsed;
534541

535542
// calculate the change in voting power used by the voter in this vote in order to accurately track the total voting power used in the funding stage
543+
// since we are moving from uint128 to uint256, we can safely assume that the value will not overflow
536544
uint256 incrementalVotingPowerUsed = uint256(cumulativeVotePowerUsed - voterPowerUsedPreVote);
537545

538546
// update accumulator for total voting power used in the funding stage in order to calculate delegate rewards
539547
currentDistribution_.fundingVotePowerCast += incrementalVotingPowerUsed;
540548

541549
// update proposal vote tracking
542-
proposal_.fundingVotesReceived += int128(voteParams_.votesUsed);
550+
proposal_.fundingVotesReceived += SafeCast.toInt128(voteParams_.votesUsed);
543551

544-
// the incremental additional votes cast on the proposal
545-
// used as a return value and emit value
546-
incrementalVotesUsed_ = uint256(Maths.abs(voteParams_.votesUsed));
552+
// the incremental additional votes cast on the proposal to be used as a return value and emit value
553+
incrementalVotesUsed_ = SafeCast.toUint256(Maths.abs(voteParams_.votesUsed));
547554

548555
// emit VoteCast instead of VoteCastWithParams to maintain compatibility with Tally
549556
// emits the amount of incremental votes cast for the proposal, not the voting power cost or total votes on a proposal
@@ -576,7 +583,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
576583
uint256 proposalId = proposal_.proposalId;
577584

578585
// update proposal votes counter
579-
proposal_.votesReceived += uint128(votes_);
586+
proposal_.votesReceived += SafeCast.toUint128(votes_);
580587

581588
// check if proposal was already screened
582589
int indexInArray = _findProposalIndex(proposalId, currentTopTenProposals);
@@ -659,6 +666,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
659666
) internal pure returns (int256 index_) {
660667
index_ = -1; // default value indicating proposalId not in the array
661668

669+
// since we are converting from uint256 to int256, we can safely assume that the value will not overflow
662670
int256 numVotesCast = int256(voteParams_.length);
663671
for (int256 i = 0; i < numVotesCast; ) {
664672
//slither-disable-next-line incorrect-equality
@@ -675,6 +683,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
675683
* @notice Sort the 10 proposals which will make it through screening and move on to the funding round.
676684
* @dev Implements the descending insertion sort algorithm.
677685
* @dev Counters incremented in an unchecked block due to being bounded by array length.
686+
* @dev Since we are converting from int256 to uint256, we can safely assume that the values will not overflow.
678687
* @param arr_ The array of proposals to sort by votes recieved.
679688
*/
680689
function _insertionSortProposalsByVotes(
@@ -713,7 +722,7 @@ abstract contract StandardFunding is Funding, IStandardFunding {
713722
uint256 numVotesCast = votesCast_.length;
714723

715724
for (uint256 i = 0; i < numVotesCast; ) {
716-
votesCastSumSquared_ += Maths.wpow(uint256(Maths.abs(votesCast_[i].votesUsed)), 2);
725+
votesCastSumSquared_ += Maths.wpow(SafeCast.toUint256(Maths.abs(votesCast_[i].votesUsed)), 2);
717726

718727
unchecked { ++i; }
719728
}

0 commit comments

Comments
 (0)