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

fix: unsafe casts better explained #275

Merged
merged 1 commit into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/core/GearStakingV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ contract GearStakingV3 is IGearStakingV3, Ownable, ReentrancyGuardTrait, SanityC
function getCurrentEpoch() public view override returns (uint16) {
if (block.timestamp < firstEpochTimestamp) return 0; // U:[GS-1]
unchecked {
// cast is safe for the next millenium
return uint16((block.timestamp - firstEpochTimestamp) / EPOCH_LENGTH) + 1; // U:[GS-1]
}
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/credit/CreditConfiguratorV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ contract CreditConfiguratorV3 is ICreditConfiguratorV3, ACLNonReentrantTrait {
uint16 feeLiquidationExpired,
uint16 liquidationDiscountExpired
) internal {
uint16 newLTUnderlying = uint16(liquidationDiscount - feeLiquidation); // I:[CC-18]
uint16 newLTUnderlying = liquidationDiscount - feeLiquidation; // I:[CC-18]
(, uint16 ltUnderlying) =
CreditManagerV3(creditManager).collateralTokenByMask({tokenMask: UNDERLYING_TOKEN_MASK});

Expand Down
11 changes: 1 addition & 10 deletions contracts/libraries/CreditLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,7 @@ library CreditLogic {
uint40 timestampRampEnd = timestampRampStart + rampDuration;
if (block.timestamp >= timestampRampEnd) return ltFinal; // U:[CL-5]

return _getRampingLiquidationThreshold(ltInitial, ltFinal, timestampRampStart, timestampRampEnd); // U:[CL-5]
}

/// @dev Computes the LT during the ramping process
function _getRampingLiquidationThreshold(
uint16 ltInitial,
uint16 ltFinal,
uint40 timestampRampStart,
uint40 timestampRampEnd
) internal view returns (uint16) {
// cast is safe since LT is between `ltInitial` and `ltFinal`, both of which are `uint16`
return uint16(
(ltInitial * (timestampRampEnd - block.timestamp) + ltFinal * (block.timestamp - timestampRampStart))
/ (timestampRampEnd - timestampRampStart)
Expand Down
8 changes: 3 additions & 5 deletions contracts/libraries/QuotasLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,20 @@
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {RAY, SECONDS_PER_YEAR, PERCENTAGE_FACTOR} from "../libraries/Constants.sol";

uint192 constant RAY_DIVIDED_BY_PERCENTAGE = uint192(RAY / PERCENTAGE_FACTOR);

/// @title Quotas logic library
library QuotasLogic {
using SafeCast for uint256;

/// @dev Computes the new interest index value, given the previous value, the interest rate, and time delta
/// @dev Unlike pool's base interest, interest on quotas is not compounding, so additive index is used
function cumulativeIndexSince(uint192 cumulativeIndexLU, uint16 rate, uint256 lastQuotaRateUpdate)
internal
view
returns (uint192)
{
// cast is safe since both summands are of the same order as `RAY` which is roughly `2**90`
return uint192(
uint256(cumulativeIndexLU)
+ RAY_DIVIDED_BY_PERCENTAGE * (block.timestamp - lastQuotaRateUpdate) * rate / SECONDS_PER_YEAR
Expand All @@ -31,7 +29,7 @@ library QuotasLogic {
pure
returns (uint128)
{
// `quoted` is `uint96`, and `cumulativeIndex / RAY` won't reach `2 ** 32` in reasonable time, so casting is safe
// cast is safe since `quoted` is `uint96` and index change is of the same order as `RAY`
return uint128(uint256(quoted) * (cumulativeIndexNow - cumulativeIndexLU) / RAY); // U:[QL-2]
}

Expand All @@ -53,7 +51,7 @@ library QuotasLogic {
unchecked {
uint96 maxQuotaCapacity = limit - totalQuoted;
// The function is never called with `requestedChange < 0`, so casting it to `uint96` is safe
// With correct configuration, `limit < type(int96).max`, so casting `maxQuotaCapacity` to `int96` is safe
// `limit` is at most `type(int96).max`, so casting `maxQuotaCapacity` to `int96` is safe
return uint96(requestedChange) > maxQuotaCapacity ? int96(maxQuotaCapacity) : requestedChange;
}
}
Expand Down
1 change: 1 addition & 0 deletions contracts/pool/GaugeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ contract GaugeV3 is IGaugeV3, ACLNonReentrantTrait {
uint256 votesCaSide = qrp.totalVotesCaSide; // U:[GA-15]
uint256 totalVotes = votesLpSide + votesCaSide; // U:[GA-15]

// cast is safe since rate is between `minRate` and `maxRate` both of which are `uint16`
rates[i] = totalVotes == 0
? qrp.minRate
: uint16((qrp.minRate * votesCaSide + qrp.maxRate * votesLpSide) / totalVotes); // U:[GA-15]
Expand Down
3 changes: 3 additions & 0 deletions contracts/pool/PoolQuotaKeeperV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ contract PoolQuotaKeeperV3 is IPoolQuotaKeeperV3, ACLNonReentrantTrait, Contract
tokenQuotaParams.totalQuoted = totalQuoted + uint96(quotaChange); // U:[PQK-15]
} else {
if (quotaChange == type(int96).min) {
// `quoted` is at most `type(int96).max` so cast is safe
quotaChange = -int96(quoted);
}

// `-quotaChange` is non-negative and at most `type(int96).max` so cast is safe
uint96 absoluteChange = uint96(-quotaChange);
newQuoted = quoted - absoluteChange;
tokenQuotaParams.totalQuoted -= absoluteChange; // U:[PQK-15]
Expand Down Expand Up @@ -208,6 +210,7 @@ contract PoolQuotaKeeperV3 is IPoolQuotaKeeperV3, ACLNonReentrantTrait, Contract
quotaRevenueChange += QuotasLogic.calcQuotaRevenueChange(rate, -int256(uint256(quoted))); // U:[PQK-16]
tokenQuotaParams.totalQuoted -= quoted; // U:[PQK-16]
accountQuota.quota = 0; // U:[PQK-16]
// `quoted` is at most `type(int96).max` so cast is safe
emit UpdateQuota({creditAccount: creditAccount, token: token, quotaChange: -int96(quoted)});
}

Expand Down
Loading