msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Wrong/Misleading NatSpec Documentation
Severity: low

Description:
The NatSpec documentation in several parts of the code base contains inaccuracies or is misleading. This issue can lead to misunderstandings about how the code functions, especially for developers who rely on these comments for clarity and guidance.\\nIn `RocketDAOProtocolProposal`, the NatSpec comments are potentially misleading:\\n```\\n/// @notice Get the votes against count of this proposal\\n/// @param _proposalID The ID of the proposal to query\\n```\\n\\n```\\n/// @notice Returns true if this proposal was supported by this node\\n/// @param _proposalID The ID of the proposal to query\\n/// @param _nodeAddress The node operator address to query\\nfunction getReceiptDirection(uint256 _proposalID, address _nodeAddress) override public view returns (VoteDirection) {\\n    return VoteDirection(getUint(keccak256(abi.encodePacked(daoProposalNameSpace, "receipt.direction", _proposalID, _nodeAddress))));\\n}\\n```\\n\\nIn RocketDAOProtocolVerifier, the NatSpec documentation is incomplete, which might leave out critical information about the function's purpose and behavior:\\n```\\n/// @notice Used by a verifier to challenge a specific index of a proposal's voting power tree\\n/// @param _proposalID The ID of the proposal being challenged\\n/// @param _index The global index of the node being challenged\\n```\\n

Impact:

Recommended mitigation:
The NatSpec documentation should be thoroughly reviewed and corrected where necessary. We recommend ensuring it accurately reflects the code's functionality and provides complete information.
Vulnerability: RocketDAOProtocolSettingsRewards.setSettingRewardClaimPeriods() Cannot Be Invoked
Severity: low

Description:
```\\nsetUint(keccak256(abi.encodePacked(settingNameSpace, "rewards.claims", "periods")), _periods);\\n```\\n

Impact:

Recommended mitigation:
To make this function useful and align it with its intended purpose, we recommend integrating its functionality into `RocketDAOProtocolProposals`. In addition, we recommend that this function emit an event upon successful change of settings, enhancing the transparency of the operation.
Vulnerability: No Protection of Uninitialized Implementation Contracts From Attacker
Severity: medium

Description:
In the contracts implement Openzeppelin's UUPS model, uninitialized implementation contract can be taken over by an attacker with `initialize` function, it's recommended to invoke the `_disableInitializers` function in the constructor to prevent the implementation contract from being used by the attacker. However all the contracts which implements `OwnablePausableUpgradeable` do not call `_disableInitializers` in the constructors\\n```\\ncontract Rewards is IRewards, OwnablePausableUpgradeable, ReentrancyGuardUpgradeable {\\n```\\n\\n```\\ncontract Pool is IPool, OwnablePausableUpgradeable, ReentrancyGuardUpgradeable {\\n```\\n\\n```\\ncontract StakedLyxToken is OwnablePausableUpgradeable, LSP4DigitalAssetMetadataInitAbstract, IStakedLyxToken, ReentrancyGuardUpgradeable {\\n```\\n\\netc.

Impact:

Recommended mitigation:
Invoke `_disableInitializers` in the constructors of contracts which implement `OwnablePausableUpgradeable` including following:\\n```\\nPool\\nPoolValidators\\nFeeEscrow\\nReward\\nStakeLyxTokem\\nOracles \\nMerkleDistributor\\n```\\n
Vulnerability: Unsafe Function receiveFees  Acknowledged
Severity: low

Description:
In the Pool contract, function `receiveFees` is used for compensate a potential penalty/slashing in the protocol by sending LYX back to the pool without minting sLYX, but the side effect is that anyone can send LYX to the pool which could mess up pool balance after all validator exited, in fact it can be replaced by a another function `receiveWithoutActivation` with access control which does the same thing.\\n```\\nfunction receiveFees() external payable override {}\\n```\\n\\n```\\nfunction receiveWithoutActivation() external payable override {\\n require(msg.sender == address(stakedLyxToken) || hasRole(DEFAULT\\_ADMIN\\_ROLE, msg.sender), "Pool: access denied");\\n}\\n```\\n

Impact:

Recommended mitigation:
Remove function `receiveFees`
Vulnerability: Unnecessary Matching in Unstake Process
Severity: low

Description:
Function `unstakeProcessed` in `StakedLyxToken` contract, when `unstakeAmount > totalPendingUnstake`, all the unstake requests should be able to be processed, thus no need to go through the matching, as a result, extra gas in the matching can be saved.\\n```\\nif (unstakeAmount > totalPendingUnstake) {\\n pool.receiveWithoutActivation{value: unstakeAmount - totalPendingUnstake}();\\n unstakeAmount = totalPendingUnstake;\\n}\\n\\ntotalPendingUnstake -= unstakeAmount;\\ntotalUnstaked += unstakeAmount;\\nuint256 amountToFill = unstakeAmount;\\n\\nfor (uint256 i = unstakeRequestCurrentIndex; i <= unstakeRequestCount; i++) {\\n UnstakeRequest storage request = \\_unstakeRequests[i];\\n if (amountToFill > (request.amount - request.amountFilled)) {\\n amountToFill -= (request.amount - request.amountFilled);\\n continue;\\n } else {\\n if (amountToFill == (request.amount - request.amountFilled) && i < unstakeRequestCount) {\\n unstakeRequestCurrentIndex = i + 1;\\n } else {\\n request.amountFilled += uint128(amountToFill);\\n unstakeRequestCurrentIndex = i;\\n }\\n break;\\n }\\n}\\n```\\n

Impact:

Recommended mitigation:
Put the matching part (line 393-411) into else branch of `if unstakeAmount > totalPendingUnstake`, change the if branch into following:\\n```\\nif (unstakeAmount > totalPendingUnstake) {\\n            pool.receiveWithoutActivation{value: unstakeAmount - totalPendingUnstake}();\\n            unstakeAmount = totalPendingUnstake;\\n            totalPendingUnstake = 0;\\n            unstakeRequestCurrentIndex = unstakeRequestCount;\\n            _unstakeRequests[unstakeRequestCount].amountFilled = _unstakeRequests[unstakeRequestCount].amount;\\n } \\n```\\n
Vulnerability: The Deployer of GovernanceTimelock Gets Privileged Access to the System.
Severity: high

Description:
The `GovernanceTimelock` contract is responsible for Roles Based Access Control management and checks in the Lybra Protocol. It offers two functions specifically that check if an address has the required role - `checkRole` and checkOnlyRole:\\n```\\nfunction checkRole(bytes32 role, address \\_sender) public view returns(bool){\\n return hasRole(role, \\_sender) || hasRole(DAO, \\_sender);\\n}\\n\\nfunction checkOnlyRole(bytes32 role, address \\_sender) public view returns(bool){\\n return hasRole(role, \\_sender);\\n}\\n```\\n\\nIn `checkRole`, the contract also lets an address with the role `DAO` bypass the check altogether, making it a powerful role.\\nFor initial role management, when the `GovernanceTimelock` contract gets deployed, its constructor logic initializes a few roles, assigns relevant admin roles, and, notably, assigns the `DAO` role to the contract, and the `DAO` and the `GOV` role to the deployer.\\n```\\nconstructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {\\n \\n \\_setRoleAdmin(DAO, GOV);\\n \\_setRoleAdmin(TIMELOCK, GOV);\\n \\_setRoleAdmin(ADMIN, GOV);\\n \\_grantRole(DAO, address(this));\\n \\_grantRole(DAO, msg.sender);\\n \\_grantRole(GOV, msg.sender);\\n}\\n```\\n\\nThe assignment of such powerful roles to a single private key with the deployer has inherent risks. Specifically in our case, the `DAO` role alone as we saw may bypass many checks within the Lybra Protocol, and the `GOV` role even has role management privileges.\\nHowever, it does make sense to assign such roles at the beginning of the deployment to finish initialization and assign the rest of the roles. One could argue that having access to the `DAO` role in the early stages of the system's life could allow for quick disaster recovery in the event of incidents as well. Though, it is still dangerous to hold privileges for such a system in a single address as we have seen over the last years in security incidents that have to do with compromised keys.

Impact:

Recommended mitigation:
While redesigning the deployment process to account for a lesser-privileged deployer would be ideal, the Lybra Finance team should at least transfer ownership as soon as the deployment is complete to minimize compromised private key risk.
Vulnerability: The configurator.getEUSDMaxLocked() Condition Can Be Bypassed During a Flashloan
Severity: medium

Description:
When converting `EUSD` tokens to `peUSD`, there is a check that limits the total amount of `EUSD` that can be converted:\\n```\\nfunction convertToPeUSD(address user, uint256 eusdAmount) public {\\n require(\\_msgSender() == user || \\_msgSender() == address(this), "MDM");\\n require(eusdAmount != 0, "ZA");\\n require(EUSD.balanceOf(address(this)) + eusdAmount <= configurator.getEUSDMaxLocked(),"ESL");\\n```\\n\\nThe issue is that there is a way to bypass this restriction. An attacker can get a flash loan (in EUSD) from this contract, essentially reducing the visible amount of locked tokens (EUSD.balanceOf(address(this))).

Impact:

Recommended mitigation:
Multiple approaches can solve this issue. One would be adding reentrancy protection. Another one could be keeping track of the borrowed amount for a flashloan.
Vulnerability: Use the Same Solidity Version Across Contracts.
Severity: low

Description:
Most contracts use the same Solidity version with `pragma solidity ^0.8.17`. The only exception is the `StakingRewardsV2` contract which has `pragma solidity ^0.8`.\\n```\\npragma solidity ^0.8;\\n```\\n

Impact:

Recommended mitigation:
If all contracts will be tested and utilized together, it would be best to utilize and document the same version within all contract code to avoid any issues and inconsistencies that may arise across Solidity versions.
Vulnerability: Missing Events.
Severity: low

Description:
In a few cases in the Lybra Protocol system, there are contracts that are missing events in significant scenarios, such as important configuration changes like a price oracle change. Consider implementing more events in the below examples.\\nNo events in the contract:\\n```\\ncontract esLBRBoost is Ownable {\\n esLBRLockSetting[] public esLBRLockSettings;\\n mapping(address => LockStatus) public userLockStatus;\\n IMiningIncentives public miningIncentives;\\n\\n // Define a struct for the lock settings\\n struct esLBRLockSetting {\\n uint256 duration;\\n uint256 miningBoost;\\n }\\n\\n // Define a struct for the user's lock status\\n struct LockStatus {\\n uint256 lockAmount;\\n uint256 unlockTime;\\n uint256 duration;\\n uint256 miningBoost;\\n }\\n\\n // Constructor to initialize the default lock settings\\n constructor(address \\_miningIncentives) {\\n```\\n\\nMissing an event during a premature unlock:\\n```\\nfunction unlockPrematurely() external {\\n require(block.timestamp + exitCycle - 3 days > time2fullRedemption[msg.sender], "ENW");\\n uint256 burnAmount = getReservedLBRForVesting(msg.sender) - getPreUnlockableAmount(msg.sender);\\n uint256 amount = getPreUnlockableAmount(msg.sender) + getClaimAbleLBR(msg.sender);\\n if (amount > 0) {\\n LBR.mint(msg.sender, amount);\\n }\\n unstakeRatio[msg.sender] = 0;\\n time2fullRedemption[msg.sender] = 0;\\n grabableAmount += burnAmount;\\n}\\n```\\n\\nMissing events for setting important configurations such as `setToken`, `setLBROracle`, and setPools:\\n```\\nfunction setToken(address \\_lbr, address \\_eslbr) external onlyOwner {\\n LBR = \\_lbr;\\n esLBR = \\_eslbr;\\n}\\n\\nfunction setLBROracle(address \\_lbrOracle) external onlyOwner {\\n lbrPriceFeed = AggregatorV3Interface(\\_lbrOracle);\\n}\\n\\nfunction setPools(address[] memory \\_vaults) external onlyOwner {\\n require(\\_vaults.length <= 10, "EL");\\n for (uint i = 0; i < \\_vaults.length; i++) {\\n require(configurator.mintVault(\\_vaults[i]), "NOT\\_VAULT");\\n }\\n vaults = \\_vaults;\\n}\\n```\\n\\nMissing events for setting important configurations such as `setRewardsDuration` and setBoost:\\n```\\n// Allows the owner to set the rewards duration\\nfunction setRewardsDuration(uint256 \\_duration) external onlyOwner {\\n require(finishAt < block.timestamp, "reward duration not finished");\\n duration = \\_duration;\\n}\\n\\n// Allows the owner to set the boost contract address\\nfunction setBoost(address \\_boost) external onlyOwner {\\n esLBRBoost = IesLBRBoost(\\_boost);\\n}\\n```\\n\\nMissing event during what is essentially staking `LBR` into `esLBR` (such as in ProtocolRewardsPool.stake()). Consider an appropriate event here such as StakeLBR:\\n```\\nif(useLBR) {\\n IesLBR(miningIncentives.LBR()).burn(msg.sender, lbrAmount);\\n IesLBR(miningIncentives.esLBR()).mint(msg.sender, lbrAmount);\\n}\\n```\\n

Impact:

Recommended mitigation:
Implement additional events as appropriate.
Vulnerability: Incorrect Interfaces
Severity: low

Description:
In a few cases, incorrect interfaces are used on top of contracts. Though the effect is the same as the contracts are just tokens and follow the same interfaces, it is best practice to implement correct interfaces.\\n`IPeUSD` is used instead of `IEUSD`\\n```\\nIPeUSD public EUSD;\\n```\\n\\n`IPeUSD` is used instead of `IEUSD`\\n```\\nif (address(EUSD) == address(0)) EUSD = IPeUSD(\\_eusd);\\n```\\n\\n`IesLBR` instead of `ILBR`\\n```\\nIesLBR public LBR;\\n```\\n\\n`IesLBR` instead of `ILBR`\\n```\\nLBR = IesLBR(\\_lbr);\\n```\\n

Impact:

Recommended mitigation:
Implement correct interfaces for consistency.
Vulnerability: Production Builds Allow Development and Localhost Origins; Snap Does Not Enforce Transport Security
Severity: medium

Description:
The snaps RPC access is restricted to certain origins only. However, there is no logic that disables development/test domains from origin checks in production builds.\\nSolflare Snap\\n../solflare-snap/src/index.js:L7-L17\\n```\\nmodule.exports.onRpcRequest = async ({ origin, request }) => {\\n if (\\n !origin ||\\n (\\n !origin.match(/^https?:\\/\\/localhost:[0-9]{1,4}$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?solflare\\.com$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?solflare\\.dev$/)\\n )\\n ) {\\n throw new Error('Invalid origin');\\n }\\n```\\n\\nAptos Snap\\n../aptos-snap/src/index.js:L6-L15\\n```\\nmodule.exports.onRpcRequest = async ({ origin, request }) => {\\n if (\\n !origin ||\\n (\\n !origin.match(/^https?:\\/\\/localhost:[0-9]{1,4}$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?risewallet\\.dev$/)\\n )\\n ) {\\n throw new Error('Invalid origin');\\n }\\n```\\n\\nSui Snap\\n../sui-snap/src/index.js:L8-L17\\n```\\nmodule.exports.onRpcRequest = async ({ origin, request }) => {\\n if (\\n !origin ||\\n (\\n !origin.match(/^https?:\\/\\/localhost:[0-9]{1,4}$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?elliwallet\\.dev$/)\\n )\\n ) {\\n throw new Error('Invalid origin');\\n }\\n```\\n

Impact:

Recommended mitigation:
Implement logic that removes development/localhost origin from the allow list for production builds. Employ strict checks on the format of provided origin. Do not by default allow all subdomains.
Vulnerability: Production Builds Allow Development and Localhost Origins; Snap Does Not Enforce Transport Security  Partially Addressed
Severity: medium

Description:
The snaps RPC access is restricted to certain origins only. However, there is no logic that disables development/test domains from origin checks in production builds.\\nSolflare Snap\\n../solflare-snap/src/index.js:L7-L17\\n```\\nmodule.exports.onRpcRequest = async ({ origin, request }) => {\\n if (\\n !origin ||\\n (\\n !origin.match(/^https?:\\/\\/localhost:[0-9]{1,4}$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?solflare\\.com$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?solflare\\.dev$/)\\n )\\n ) {\\n throw new Error('Invalid origin');\\n }\\n```\\n\\nAptos Snap\\n../aptos-snap/src/index.js:L6-L15\\n```\\nmodule.exports.onRpcRequest = async ({ origin, request }) => {\\n if (\\n !origin ||\\n (\\n !origin.match(/^https?:\\/\\/localhost:[0-9]{1,4}$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?risewallet\\.dev$/)\\n )\\n ) {\\n throw new Error('Invalid origin');\\n }\\n```\\n\\nSui Snap\\n../sui-snap/src/index.js:L8-L17\\n```\\nmodule.exports.onRpcRequest = async ({ origin, request }) => {\\n if (\\n !origin ||\\n (\\n !origin.match(/^https?:\\/\\/localhost:[0-9]{1,4}$/) &&\\n !origin.match(/^https?:\\/\\/(?:\\S+\\.)?elliwallet\\.dev$/)\\n )\\n ) {\\n throw new Error('Invalid origin');\\n }\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nThe client has issued the following statement:\\nChangesets:\\nsolflare-wallet/solflare-snap@749d2b0\\nsolflare-wallet/aptos-snap@eef10b5\\nsolflare-wallet/sui-snap@898295f\\nStatement from the Assessment Team:\\nImplement logic that removes development/localhost origin from the allow list for production builds. Employ strict checks on the format of provided origin. Do not by default allow all subdomains.
Vulnerability: All Roles Are Set to the Same Account.
Severity: low

Description:
From talking to the team we know that all roles will be held by different timelock contracts. In the code they all are initiated to the same `admin` address. That would mean that most roles would need to be transferred. Given that each transfer take 2 transactions and there are 3 roles to transfer that would equate to 6 transactions just to properly set up the contract on deployment. That also increments the time it would take and space for making errors.\\nIt is also should be noted that the `regulator` role is not being initialized there at all.\\n```\\n// solhint-disable-next-line func-name-mixedcase\\nfunction \\_\\_DramAccessControl\\_init\\_unchained(\\n address admin\\n) internal onlyInitializing {\\n \\_grantRole(ADMIN\\_ROLE, admin);\\n \\_grantRole(ROLE\\_MANAGER\\_ROLE, admin);\\n \\_grantRole(SUPPLY\\_MANAGER\\_ROLE, admin);\\n}\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nAll roles, including regulatory manager, are now set to different accounts. The modification can be found in commit `b70348e6998e35282212243ea639d174ced1ef2d`\\nWe suggest passing several addresses into the constructor and setting them to the correct addresses right away. Alternatively one can not set them at all and grant those roles later in order to avoid revoking the roles that admin should not have, such as `SUPPLY_MANAGER_ROLE`.
Vulnerability: Setting MintCap to a Specific Value Is Prone to Front-Running.
Severity: low

Description:
`Dram` stable coin is using the approval-like model to set the minting caps of different operators, thus it is prone to the same front-run issues as the approval mechanism. When using the `setMintCap` function directly operator could front-run the transaction and completely spend the old cap and then spend the new one again after setting the transaction goes through.\\n```\\nfunction setMintCap(\\n address operator,\\n uint256 amount\\n) external onlyRoleOrAdmin(ROLE\\_MANAGER\\_ROLE) {\\n \\_setMintCap(operator, amount);\\n}\\n```\\n\\nImagine the following scenario:\\nAlice has a mint cap of 10.\\nA transaction is sent to the mem-pool to set it to 5 (decrease the cap). The intent is that Alice should only be able to mint 5 tokens.\\nAlice frontruns this transaction and mints 10 tokens.\\nOnce transaction 2 goes through Alice mints 5 more tokens.\\nIn total Alice minted 15 tokens.

Impact:

Recommended mitigation:
Avoid using setting the specific mint caps and rather use increase/decrease methods that are present in the code already.
Vulnerability: Incorrect Priviliges setOperatorAddresses  Acknowledged
Severity: high

Description:
The function `setOperatorAddresses` instead of allowing the Operator to update its own, as well as the Fee Recipient address, incorrectly provides the privileges to the Fee Recipient. As a result, the Fee Recipient can modify the operator address as and when needed, to DoS the operator and exploit the system. Additionally, upon reviewing the documentation, we found that there are no administrative rights defined for the Fee Recipient, hence highlighting the incorrect privilege allocation.\\n```\\nfunction setOperatorAddresses(\\n uint256 \\_operatorIndex,\\n address \\_operatorAddress,\\n address \\_feeRecipientAddress\\n) external onlyActiveOperatorFeeRecipient(\\_operatorIndex) {\\n \\_checkAddress(\\_operatorAddress);\\n \\_checkAddress(\\_feeRecipientAddress);\\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\\n\\n operators.value[\\_operatorIndex].operator = \\_operatorAddress;\\n operators.value[\\_operatorIndex].feeRecipient = \\_feeRecipientAddress;\\n emit ChangedOperatorAddresses(\\_operatorIndex, \\_operatorAddress, \\_feeRecipientAddress);\\n}\\n```\\n

Impact:

Recommended mitigation:
The modifier should be `onlyActiveOperatorOrAdmin` allowing only the operator itself or admin of the system, to update the necessary addresses.\\nAlso, for transferring crucial privileges from one address to another, the operator's address should follow a 2-step approach like transferring ownership.
Vulnerability: Unconstrained Snapshot While Setting Operator Limit
Severity: medium

Description:
Function `setOperatorLimit` as the name says, allows the `SYS_ADMIN` to set/update the staking limit for an operator. The function ensures that if the limit is being increased, the `_snapshot` must be ahead of the last validator edit(block.number at which the last validator edit occurred). However, the parameter `_snapshot` is unconstrained and can be any number. Also, the functions `addValidators` and `removeValidators` update the `block.number` signifying the last validator edit, but never constrain the new edits with it. Since there are no publicly available functions to access this value, makes the functionality even more confusing and may be unnecessary.\\n```\\nif (\\n operators.value[\\_operatorIndex].limit < \\_limit &&\\n StakingContractStorageLib.getLastValidatorEdit() > \\_snapshot\\n) {\\n revert LastEditAfterSnapshot();\\n}\\n```\\n

Impact:

Recommended mitigation:
If the functionality is not needed, consider removing it. Otherwise, add some necessary logic to either constrain the last validator edit or add public functions for the users to access it.
Vulnerability: Hardcoded Operator Limit Logic
Severity: medium

Description:
The contract defines some hardcoded limits which is not the right approach for upgradeable contracts and opens doors for accidental mistakes, if not handled with care.\\nThe operators for the current version are limited to 1. If the auditee team decides to open the system to work with more operators but fails to change the limit while upgrading, the upgraded contract will have no effect, and will still disallow any more operators to be added.\\n```\\nfunction addOperator(address \\_operatorAddress, address \\_feeRecipientAddress) external onlyAdmin returns (uint256) {\\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\\n StakingContractStorageLib.OperatorInfo memory newOperator;\\n\\n if (operators.value.length == 1) {\\n revert MaximumOperatorCountAlreadyReached();\\n }\\n```\\n\\nAlso, the function `_depositOnOneOperator` hardcodes the operator Index as 0 since the contract only supports one operator.\\n```\\nfunction \\_depositOnOneOperator(uint256 \\_depositCount, uint256 \\_totalAvailableValidators) internal {\\n StakingContractStorageLib.setTotalAvailableValidators(\\_totalAvailableValidators - \\_depositCount);\\n \\_depositValidatorsOfOperator(0, \\_depositCount);\\n}\\n```\\n

Impact:

Recommended mitigation:
A better approach could be to constrain the limit of operators that can be added with a storage variable or constant, provided at the time of contract initialization. The contract should also consider supporting dynamic operator deposits for future versions instead of the default hardcoded index.