Platform: Code4rena
Start Date: 31/03/2022
Pot Size: $75,000 USDC
Total HM: 7
Participants: 42
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 5
Id: 102
League: ETH
Rank: 4/42
Findings: 3
Award: $2,790.92
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: robee
Also found by: IllIllI, cmichel, georgypetrov
I'm upgrading the following issue from a QA report (issue https://github.com/code-423n4/2022-03-volt-findings/issues/48 ) to Medium risk:
calculateDeviationThresholdBasisPoints()
was important enough to be in a separate library rather than being just a normal function of another contract so it should be generic enough for other contracts to use it. If the input argument a
is zero then the function performs a division by zero and will throw an exception. If this behavior is what is wanted, the NatSpec should make this explicit and a revert()
should be added with an appropriate error message.
/// @notice return the percent deviation between a and b in basis points terms function calculateDeviationThresholdBasisPoints(int256 a, int256 b) internal pure returns (uint256) { int256 delta = a - b; int256 basisPoints = (delta * Constants.BP_INT) / a;
#0 - jack-the-pug
2022-05-03T03:10:27Z
🌟 Selected for report: rayn
Also found by: 0xDjango, 0xkatana, 0xkowloon, BouSalman, CertoraInc, Dravee, Funen, Hawkeye, IllIllI, Jujic, Kenshin, Kthere, Meta0xNull, Sleepy, TerrierLover, async, aysha, berndartmueller, catchup, cccz, cmichel, csanuragjain, danb, defsec, georgypetrov, hake, hubble, kenta, kyliek, pauliax, rfa, robee, sahar, shenwilly, teryanarmen
733.0801 USDC - $733.08
calculateDeviationThresholdBasisPoints()
was important enough to be in a separate library rather than being just a normal function of another contract so it should be generic enough for other contracts to use it. If the input argument a
is zero then the function performs a division by zero and will throw an exception. If this behavior is what is wanted, the NatSpec should make this explicit and a revert()
should be added with an appropriate error message.
/// @notice return the percent deviation between a and b in basis points terms function calculateDeviationThresholdBasisPoints(int256 a, int256 b) internal pure returns (uint256) { int256 delta = a - b; int256 basisPoints = (delta * Constants.BP_INT) / a;
decimals()
decimals()
was optional in the original ERC20 specification, so there are functions that do not implement it. It's not safe to cast arbitrary token addresses in order to call this function. If IERC20Metadata
is to be relied on, that should be the variable type of the token variable, rather than it being address
, so the compiler can verify that types correctly match, rather than this being a runtime failure. See this prior instance of this issue which was marked as Low risk. Do this to resolve the issue.
int256(uint256(IERC20Metadata(token).decimals()));
See this issue from a prior contest for details.
bytes32 public DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) public nonces; /// @notice Fei token constructor /// @param core Fei Core address to reference constructor(address core) ERC20("VOLT", "VOLT") CoreRef(core) { uint256 chainId; // solhint-disable-next-line no-inline-assembly assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name())), keccak256(bytes("1")), chainId, address(this) ) ); }
address(0x0)
when assigning values to address
state variablesoracle = _oracle;
The NatSpec says that the code is doing an interpolation, which is finding a value between two endpoints. The code however is doing extrapolation - projecting a future value based on prior values. From the judging criteria guidelines: Low: Assets are not at risk. State handling, function incorrect as to spec, issues with comments.
/// @notice contract that receives a chainlink price feed and then linearly interpolates that rate over /// a 28 day period into the VOLT price. Interest is compounded monthly when the rate is updated
_;
It is dangerous to have _;
only be executed from some code paths. If the function using it has return
values, they'll remain uninitialized, which can lead to unexpected behavior
modifier ifMinterSelf() { if (_core.isMinter(address(this))) { _; } }
The variable name amountFeiToTransfer
is misleading because it is not only Fei. It should be named something like amountStablecoinToTransfer
The code refers to both CPI and CPI-U in various places, using CPI-U for calls to getMonthlyAPR()
. It should consistently use one or the other in all places
/// ---------- Mutable CPI Variables Packed Into Single Storage Slot to Save an SSTORE & SLOAD ----------
/// @notice the current month's CPI data
/// @notice the previous month's CPI data
/// @notice the time frame over which all changes in CPI data are applied
/// @notice job id that retrieves the latest CPI data
/// calculate new monthly CPI-U rate in basis points based on current and previous month
/// @param _cpiData latest CPI data from BLS
/// @param _cpiData latest CPI data from BLS
/// store CPI data, removes stale data
/// calculate new monthly CPI-U rate in basis points
CPI is an index not an interest rate and is monthly not annual, so using 'APR' is misleading. Rename to something like getMonthOverMonthPercentageChange()
/// @notice get APR from chainlink data by measuring (current month - previous month) / previous month /// @return percentageChange percentage change in basis points over past month function getMonthlyAPR() public view returns (int256 percentageChange) {
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
function update() public {}
constant
s should be defined rather than using magic numbersprice = Decimal.from(currentPrice).div(1e18);
if (chainId == 1 || chainId == 42) {
getDay(block.timestamp) > 14,
int256 feiDecimals = 18;
Use a solidity version of at least 0.8.12 to get string.concat()
to be used instead of abi.encodePacked(<str>,<str>)
pragma solidity ^0.8.4;
const
/immutable
variablesIf the variable needs to be different based on which class it comes from, a view
/pure
function should be used instead (e.g. like this).
bytes32 public DOMAIN_SEPARATOR;
bytes32 public override CONTRACT_ADMIN_ROLE;
pragma solidity ^0.8.0;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.0;
pragma solidity ^0.8.4;
/// @notice overriden function in the price bound PSM
overriden -> overridden
/// @param rateLimitedAddress the address whose buffer will be depleted /// @param amount the amount to remove from the rateLimitedAddress's buffer function _depleteIndividualBuffer( address rateLimitedAddress, uint256 amount ) internal returns (uint256) {
Missing: @return
/// @notice permit spending of FEI /// @param owner the FEI holder /// @param spender the approved operator /// @param value the amount approved /// @param deadline the deadline after which the approval is no longer valid function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s
Missing: @param v
@param r
@param s
/// @param amountVoltIn the amount of VOLT to sell /// @param minAmountOut the minimum amount out otherwise the TX will fail function redeem( address to, uint256 amountVoltIn, uint256 minAmountOut ) external virtual override nonReentrant whenNotPaused whileRedemptionsNotPaused returns (uint256 amountOut)
Missing: @return
/// @param amountIn the amount of external asset to sell to the PSM /// @param minVoltAmountOut the minimum amount of VOLT out otherwise the TX will fail function mint( address to, uint256 amountIn, uint256 minVoltAmountOut ) external virtual override nonReentrant whenNotPaused whileMintingNotPaused returns (uint256 amountVoltOut)
Missing: @return
indexed
fieldsEach event
should use three indexed
fields if there are three or more fields
event DurationUpdate(uint256 oldDuration, uint256 newDuration);
event TimerReset(uint256 startTime);
event DeviationThresholdUpdate(uint256 oldThreshold, uint256 newThreshold);
event BufferUsed(uint256 amountUsed, uint256 bufferRemaining);
event BufferCapUpdate(uint256 oldBufferCap, uint256 newBufferCap);
event RateLimitPerSecondUpdate( uint256 oldRateLimitPerSecond, uint256 newRateLimitPerSecond );
All multiplications should be done first before doing any divisions, to avoid loss of precision
ScalingPriceOracle.getCurrentOraclePrice() (contracts/oracle/ScalingPriceOracle.sol#110-118) performs a multiplication on the result of a division: -pricePercentageChange = oraclePriceInt * monthlyChangeRateBasisPoints / Constants.BP_INT (contracts/oracle/ScalingPriceOracle.sol#114) -priceDelta = pricePercentageChange * timeDelta / TIMEFRAME.toInt256() (contracts/oracle/ScalingPriceOracle.sol#115) OracleRef.readOracle() (contracts/refs/OracleRef.sol#101-123) performs a multiplication on the result of a division: -_peg = _peg.div(scalingFactor) (contracts/refs/OracleRef.sol#112) -_peg = _peg.mul(scalingFactor) (contracts/refs/OracleRef.sol#115) Deviation.calculateDeviationThresholdBasisPoints(int256,int256) (contracts/utils/Deviation.sol#17-26) performs a multiplication on the result of a division: -basisPoints = (delta * Constants.BP_INT) / a (contracts/utils/Deviation.sol#23) -(basisPoints * - 1).toUint256() (contracts/utils/Deviation.sol#25)
Follow the best-practice of the Checks-Effects-Interactions pattern
Reentrancy in NonCustodialPSM.mint(address,uint256,uint256) (contracts/peg/NonCustodialPSM.sol#259-303): External calls: - updateOracle() (contracts/peg/NonCustodialPSM.sol#272) - oracle.update() (contracts/refs/OracleRef.sol#95) - underlyingToken.safeTransferFrom(msg.sender,address(pcvDeposit),amountIn) (contracts/peg/NonCustodialPSM.sol#280-284) - IERC20(volt()).safeTransfer(to,amountFeiToTransfer) (contracts/peg/NonCustodialPSM.sol#293) - rateLimitedMinter.mintVolt(to,amountFeiToMint) (contracts/peg/NonCustodialPSM.sol#297) State variables written after the call(s): - _replenishBuffer(amountVoltOut) (contracts/peg/NonCustodialPSM.sol#300) - bufferStored = Math.min(newBuffer + amount,_bufferCap) (contracts/utils/RateLimited.sol#128)
Reentrancy in PCVDeposit._withdrawERC20(address,address,uint256) (contracts/pcv/PCVDeposit.sol#25-32): External calls: - IERC20(token).safeTransfer(to,amount) (contracts/pcv/PCVDeposit.sol#30) Event emitted after the call(s): - WithdrawERC20(msg.sender,token,to,amount) (contracts/pcv/PCVDeposit.sol#31) Reentrancy in ERC20CompoundPCVDeposit.deposit() (contracts/pcv/compound/ERC20CompoundPCVDeposit.sol#28-40): External calls: - token.approve(address(cToken),amount) (contracts/pcv/compound/ERC20CompoundPCVDeposit.sol#31) - require(bool,string)(CErc20(address(cToken)).mint(amount) == 0,ERC20CompoundPCVDeposit: deposit error) (contracts/pcv/compound/ERC20CompoundPCVDeposit.sol#34-37) Event emitted after the call(s): - Deposit(msg.sender,amount) (contracts/pcv/compound/ERC20CompoundPCVDeposit.sol#39) Reentrancy in NonCustodialPSM.mint(address,uint256,uint256) (contracts/peg/NonCustodialPSM.sol#259-303): External calls: - updateOracle() (contracts/peg/NonCustodialPSM.sol#272) - oracle.update() (contracts/refs/OracleRef.sol#95) - underlyingToken.safeTransferFrom(msg.sender,address(pcvDeposit),amountIn) (contracts/peg/NonCustodialPSM.sol#280-284) - IERC20(volt()).safeTransfer(to,amountFeiToTransfer) (contracts/peg/NonCustodialPSM.sol#293) - rateLimitedMinter.mintVolt(to,amountFeiToMint) (contracts/peg/NonCustodialPSM.sol#297) Event emitted after the call(s): - Mint(to,amountIn,amountVoltOut) (contracts/peg/NonCustodialPSM.sol#302) Reentrancy in NonCustodialPSM.redeem(address,uint256,uint256) (contracts/peg/NonCustodialPSM.sol#219-251): External calls: - updateOracle() (contracts/peg/NonCustodialPSM.sol#234) - oracle.update() (contracts/refs/OracleRef.sol#95) - IERC20(volt()).safeTransferFrom(msg.sender,address(this),amountVoltIn) (contracts/peg/NonCustodialPSM.sol#242-246) - pcvDeposit.withdraw(to,amountOut) (contracts/peg/NonCustodialPSM.sol#248) Event emitted after the call(s): - Redeem(to,amountVoltIn,amountOut) (contracts/peg/NonCustodialPSM.sol#250) Reentrancy in CompoundPCVDepositBase.withdraw(address,uint256) (contracts/pcv/compound/CompoundPCVDepositBase.sol#38-50): External calls: - require(bool,string)(cToken.redeemUnderlying(amountUnderlying) == 0,CompoundPCVDeposit: redeem error) (contracts/pcv/compound/CompoundPCVDepositBase.sol#44-47) Event emitted after the call(s): - Withdrawal(msg.sender,to,amountUnderlying) (contracts/pcv/compound/CompoundPCVDepositBase.sol#49) Reentrancy in NonCustodialPSM.withdrawERC20(address,address,uint256) (contracts/peg/NonCustodialPSM.sol#201-208): External calls: - IERC20(token).safeTransfer(to,amount) (contracts/peg/NonCustodialPSM.sol#206) Event emitted after the call(s): - WithdrawERC20(msg.sender,token,to,amount) (contracts/peg/NonCustodialPSM.sol#207) Reentrancy in PCVDeposit.withdrawETH(address,uint256) (contracts/pcv/PCVDeposit.sol#37-45): External calls: - Address.sendValue(to,amountOut) (contracts/pcv/PCVDeposit.sol#43) Event emitted after the call(s): - WithdrawETH(msg.sender,to,amountOut) (contracts/pcv/PCVDeposit.sol#44)
#0 - jack-the-pug
2022-05-03T03:17:13Z
I have upgraded the following finding as Medium risk, so I have created a separate issue (https://github.com/code-423n4/2022-03-volt-findings/issues/130) for it for judging and awarding purposes:
#1 - jack-the-pug
2022-05-13T15:34:11Z
Besides the first issue (upgraded to Medium), I find the risk level of other issues suitable.
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0xNazgul, 0xkatana, 0xkowloon, CertoraInc, Dravee, Funen, Hawkeye, Jujic, Kenshin, Meta0xNull, Sleepy, TerrierLover, catchup, csanuragjain, defsec, georgypetrov, kenta, okkothejawa, rayn, rfa, robee, saian, samruna
748.329 USDC - $748.33
The isTimeEnded()
function does a lot of calculations every time it's called. It's called from multiple modifiers so it's important for it to be efficient. Rather than storing the duration, the code can calculate and store the ending timestamp, so isTimeEnded()
can just be a direct comparison of two uint256
s. The duration can be calculated by subtracting the start timestamp from the ending timestamp.
/// @notice return true if time period has ended function isTimeEnded() public view returns (bool) { return remainingTime() == 0; } /// @notice number of seconds remaining until time is up /// @return remaining function remainingTime() public view returns (uint256) { return duration - timeSinceStart(); // duration always >= timeSinceStart which is on [0,d] } /// @notice number of seconds since contract was initialized /// @return timestamp /// @dev will be less than or equal to duration function timeSinceStart() public view returns (uint256) { if (!isTimeStarted()) { return 0; // uninitialized } uint256 _duration = duration; uint256 timePassed = block.timestamp - startTime; // block timestamp always >= startTime return timePassed > _duration ? _duration : timePassed; }
RateLimited.sol
and MultiRateLimited.sol
The functionality of RateLimited.sol
can be achieved by using either address(0)
or address(this)
as the rateLimitedAddress
so having a separate RateLimited.sol
contract is a waste of deployment gas.
require()
/revert()
strings longer than 32 bytes cost extra gasrequire( CErc20(address(cToken)).mint(amount) == 0, "ERC20CompoundPCVDeposit: deposit error" );
require( getDay(block.timestamp) > 14, "ScalingPriceOracle: cannot request data before the 15th" );
require( MAXORACLEDEVIATION.isWithinDeviationThreshold( currentMonth.toInt256(), _cpiData.toInt256() ), "ScalingPriceOracle: Chainlink data outside of deviation threshold" );
require( _individualMaxBufferCap < _globalBufferCap, "MultiRateLimited: max buffer cap invalid" );
require( rateLimitPerAddress[rateLimitedAddress].lastBufferUsedTime != 0, "MultiRateLimited: rate limit address does not exist" );
require( newRateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "MultiRateLimited: exceeds global max rate limit per second" );
require( newBufferCap <= bufferCap, "MultiRateLimited: exceeds global buffer cap" );
require( _rateLimitPerSecond <= individualMaxRateLimitPerSecond, "MultiRateLimited: rate limit per second exceeds non governor allowable amount" );
require( _bufferCap <= individualMaxBufferCap, "MultiRateLimited: max buffer cap exceeds non governor allowable amount" );
require( _bufferCap <= bufferCap, "MultiRateLimited: buffercap too high" );
require( rateLimitData.lastBufferUsedTime != 0, "MultiRateLimited: rate limit address does not exist" );
require( _rateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "MultiRateLimited: rateLimitPerSecond too high" );
require( _bufferCap <= bufferCap, "MultiRateLimited: new buffercap too high" );
require( rateLimitPerAddress[rateLimitedAddress].lastBufferUsedTime == 0, "MultiRateLimited: address already added" );
require( _rateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "MultiRateLimited: rateLimitPerSecond too high" );
require(newBuffer != 0, "MultiRateLimited: no rate limit buffer");
require( _rateLimitPerSecond <= _maxRateLimitPerSecond, "RateLimited: rateLimitPerSecond too high" );
require( newRateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "RateLimited: rateLimitPerSecond too high" );
require(newBuffer != 0, "RateLimited: no rate limit buffer");
require( _core.isPCVController(msg.sender), "CoreRef: Caller is not a PCV controller" );
require( _core.isGovernor(msg.sender) || isContractAdmin(msg.sender), "CoreRef: Caller is not a governor or contract admin" );
require( _core.isGovernor(msg.sender), "CoreRef: Caller is not a governor" );
require( _core.isGovernor(msg.sender) || _core.isGuardian(msg.sender), "CoreRef: Caller is not a guardian or governor" );
require( _core.isGovernor(msg.sender) || _core.isGuardian(msg.sender) || isContractAdmin(msg.sender), "CoreRef: Caller is not governor or guardian or admin" );
require( isGovernor(msg.sender), "Permissions: Caller is not a governor" );
require( isGuardian(msg.sender), "Permissions: Caller is not a guardian" );
require( role != GOVERN_ROLE, "Permissions: Guardian cannot revoke governor" );
require(!redeemPaused, "PegStabilityModule: Redeem paused");
require(!mintPaused, "PegStabilityModule: Minting paused");
require( amountOut >= minAmountOut, "PegStabilityModule: Redeem not enough out" );
require( amountVoltOut >= minVoltAmountOut, "PegStabilityModule: Mint not enough out" );
require( address(newMinter) != address(0), "PegStabilityModule: Invalid new GlobalRateLimitedMinter" );
require( newMintFeeBasisPoints <= MAX_FEE, "PegStabilityModule: Mint fee exceeds max fee" );
require( newRedeemFeeBasisPoints <= MAX_FEE, "PegStabilityModule: Redeem fee exceeds max fee" );
require( address(newPCVDeposit) != address(0), "PegStabilityModule: Invalid new PCVDeposit" );
require( newPCVDeposit.balanceReportedIn() == address(underlyingToken), "PegStabilityModule: Underlying token mismatch" );
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
pragma solidity ^0.8.4;
Use a solidity version of at least 0.8.2 to get compiler automatic inlining
Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require()
strings
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
pragma solidity ^0.8.0;
pragma solidity ^0.8.0;
bool
s for storage incurs overhead// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
bool public doPartialAction;
bool public override doInvert;
bool public redeemPaused;
bool public mintPaused;
internal
functions only called once can be inlined to save gasfunction _updateCPIData(uint256 _cpiData) internal {
function _addNewMonth(uint128 newMonth) internal {
function _setupGovernor(address governor) internal {
The instances below point to the second access of a state variable within a function. Less obvious optimizations include having local storage variables of mappings within state variable mappings or mappings within state variable structs, having local storage variables of structs within mappings, or having local caches of state variable contracts/addresses.
require(cToken.isCToken(), "CompoundPCVDeposit: Not a cToken");
(cToken.balanceOf(address(this)) * exchangeRate) /
token.approve(address(cToken), amount);
percentageChange = (delta * Constants.BP_INT) / int128(previousMonth);
rateLimitPerAddress[rateLimitedAddress].lastBufferUsedTime = block
emit BufferUsed(usedAmount, bufferStored);
(_peg, valid) = backupOracle.read();
scalingFactor = 10**(-1 * decimalsNormalizer).toUint256();
require()
statements that use &&
saves gasSee this issue for an example
require( recoveredAddress != address(0) && recoveredAddress == owner, "Fei: INVALID_SIGNATURE" );
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadWhen using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed
uint128 public currentMonth;
uint128 public previousMonth;
uint128 _currentMonth,
uint128 _previousMonth
function _addNewMonth(uint128 newMonth) internal {
uint32 lastBufferUsedTime;
uint112 bufferCap;
uint112 bufferStored;
uint112 rateLimitPerSecond;
uint112 _rateLimitPerSecond,
uint112 _bufferCap
uint112 _rateLimitPerSecond,
uint112 _bufferCap
returns (uint112)
uint112 _rateLimitPerSecond,
uint112 _bufferCap
uint112 oldRateLimitPerSecond = rateLimitData.rateLimitPerSecond;
uint112 _rateLimitPerSecond,
uint112 _bufferCap
uint8 v,
keccak256()
, should use immutable
rather than constant
See this issue for a detail description of the issue
bytes32 internal constant GOVERNOR = keccak256("GOVERN_ROLE");
bytes32 internal constant GUARDIAN = keccak256("GUARDIAN_ROLE");
bytes32 internal constant PCV_CONTROLLER = keccak256("PCV_CONTROLLER_ROLE");
bytes32 internal constant MINTER = keccak256("MINTER_ROLE");
bytes32 internal constant PARAMETER_ADMIN = keccak256("PARAMETER_ADMIN");
bytes32 internal constant ORACLE_ADMIN = keccak256("ORACLE_ADMIN_ROLE");
bytes32 internal constant TRIBAL_CHIEF_ADMIN = keccak256("TRIBAL_CHIEF_ADMIN_ROLE");
bytes32 internal constant PCV_GUARDIAN_ADMIN = keccak256("PCV_GUARDIAN_ADMIN_ROLE");
bytes32 internal constant MINOR_ROLE_ADMIN = keccak256("MINOR_ROLE_ADMIN");
bytes32 internal constant FUSE_ADMIN = keccak256("FUSE_ADMIN");
bytes32 internal constant VETO_ADMIN = keccak256("VETO_ADMIN");
bytes32 internal constant MINTER_ADMIN = keccak256("MINTER_ADMIN");
bytes32 internal constant OPTIMISTIC_ADMIN = keccak256("OPTIMISTIC_ADMIN");
bytes32 internal constant LBP_SWAP_ROLE = keccak256("SWAP_ADMIN_ROLE");
bytes32 internal constant VOTIUM_ROLE = keccak256("VOTIUM_ADMIN_ROLE");
bytes32 internal constant MINOR_PARAM_ROLE = keccak256("MINOR_PARAM_ROLE");
bytes32 internal constant ADD_MINTER_ROLE = keccak256("ADD_MINTER_ROLE");
bytes32 internal constant PSM_ADMIN_ROLE = keccak256("PSM_ADMIN_ROLE");
bytes32 public constant override BURNER_ROLE = keccak256("BURNER_ROLE");
bytes32 public constant override MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant override PCV_CONTROLLER_ROLE = keccak256("PCV_CONTROLLER_ROLE");
bytes32 public constant override GOVERN_ROLE = keccak256("GOVERN_ROLE");
bytes32 public constant override GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
private
rather than public
for constants, saves gasIf needed, the value can be read from the verified contract source code
uint256 public constant override TIMEFRAME = 28 days;
uint256 public constant override MAXORACLEDEVIATION = 2_000;
bytes32 public immutable jobId;
uint256 public immutable fee;
uint256 public immutable MAX_RATE_LIMIT_PER_SECOND;
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
bytes32 public constant override BURNER_ROLE = keccak256("BURNER_ROLE");
bytes32 public constant override MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant override PCV_CONTROLLER_ROLE = keccak256("PCV_CONTROLLER_ROLE");
bytes32 public constant override GOVERN_ROLE = keccak256("GOVERN_ROLE");
bytes32 public constant override GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
uint256 public immutable override MAX_FEE = 300;
require()
/revert()
checks should be refactored to a modifier or functionrequire( _rateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "MultiRateLimited: rateLimitPerSecond too high" );
immutable
IERC20 public token;
require()
or revert()
statements that check input arguments should be at the top of the functionChecks that involve constants should come before checks that involve state variables
require( _rateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "MultiRateLimited: rateLimitPerSecond too high" );
require( _rateLimitPerSecond <= MAX_RATE_LIMIT_PER_SECOND, "MultiRateLimited: rateLimitPerSecond too high" );
require( _rateLimitPerSecond <= _maxRateLimitPerSecond, "RateLimited: rateLimitPerSecond too high" );
revert()
/require()
strings to save deployment gasreturn sendChainlinkRequestTo(oracle, request, fee);