msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Mint Function in the Infinity Pool Will Emit the Incorrect Value.
Severity: low

Description:
In the `InifinityPool` file the `mint` function recomputes the amount of the assets before emitting the event. While this is fine in a lot of cases, that will not always be true. The result of `previewMint` and `convertToAssets` will only be equal while the `totalAssets` and `totalSupply` are equal. For example, this assumption will break after the first liquidation.\\n```\\nfunction mint(uint256 shares, address receiver) public isOpen returns (uint256 assets) {\\n if(shares == 0) revert InvalidParams();\\n // These transfers need to happen before the mint, and this is forcing a higher degree of coupling than is ideal\\n assets = previewMint(shares);\\n asset.transferFrom(msg.sender, address(this), assets);\\n liquidStakingToken.mint(receiver, shares);\\n assets = convertToAssets(shares);\\n emit Deposit(msg.sender, receiver, assets, shares);\\n}\\n```\\n

Impact:

Recommended mitigation:
Use the `assets` value computed by the `previewMint` when emitting the event.
Vulnerability: Potential Overpayment Due to Rounding Imprecision  Won't Fix
Severity: low

Description:
Inside the `InifintyPool` the `pay` function might accept unaccounted files. Imagine a situation where an Agent is trying to repay only the fees portion of the debt. In that case, the following branch will be executed:\\n```\\nif (vc.value <= interestOwed) {\\n // compute the amount of epochs this payment covers\\n // vc.value is not WAD yet, so divWadDown cancels the extra WAD in interestPerEpoch\\n uint256 epochsForward = vc.value.divWadDown(interestPerEpoch);\\n // update the account's `epochsPaid` cursor\\n account.epochsPaid += epochsForward;\\n // since the entire payment is interest, the entire payment is used to compute the fee (principal payments are fee-free)\\n feeBasis = vc.value;\\n} else {\\n```\\n\\nThe issue is if the `value` does not divide by the `interestPerEpoch` exactly, any remainder will remain in the InfinityPool.\\n```\\nuint256 epochsForward = vc.value.divWadDown(interestPerEpoch);\\n```\\n

Impact:

Recommended mitigation:
Since the remainder will most likely not be too large this is not critical, but ideally, those remaining funds would be included in the `refund` variable.
Vulnerability: jumpStartAccount Should Be Subject to the Same Approval Checks as Regular Borrow.
Severity: low

Description:
`InfinityPool` contract has the ability to kick start an account that will have a debt position in this pool.\\n```\\nfunction jumpStartAccount(address receiver, uint256 agentID, uint256 accountPrincipal) external onlyOwner {\\n Account memory account = \\_getAccount(agentID);\\n // if the account is already initialized, revert\\n if (account.principal != 0) revert InvalidState();\\n // create the account\\n account.principal = accountPrincipal;\\n account.startEpoch = block.number;\\n account.epochsPaid = block.number;\\n // save the account\\n account.save(router, agentID, id);\\n // add the pool to the agent's list of borrowed pools\\n GetRoute.agentPolice(router).addPoolToList(agentID, id);\\n // mint the iFIL to the receiver, using principal as the deposit amount\\n liquidStakingToken.mint(receiver, convertToShares(accountPrincipal));\\n // account for the new principal in the total borrowed of the pool\\n totalBorrowed += accountPrincipal;\\n}\\n```\\n

Impact:

Recommended mitigation:
We suggest that this action is subject to the same rules as the standard borrow action. Thus checks on DTE, LTV and DTI should be done if possible.
Vulnerability: StrategyBase - Inflation Attack Prevention Can Lead to Stuck Funds
Severity: low

Description:
As a defense against what has come to be known as inflation or donation attack in the context of ERC-4626, the `StrategyBase` contract - from which concrete strategy implementations are supposed to inherit - enforces that the amount of shares in existence for a particular strategy is always either 0 or at least a certain minimum amount that is set to 10^9. This mitigates inflation attacks, which require a small total supply of shares to be effective.\\n```\\nuint256 updatedTotalShares = totalShares + newShares;\\nrequire(updatedTotalShares >= MIN\\_NONZERO\\_TOTAL\\_SHARES,\\n    "StrategyBase.deposit: updated totalShares amount would be nonzero but below MIN\\_NONZERO\\_TOTAL\\_SHARES");\\n```\\n\\n```\\n// Calculate the value that `totalShares` will decrease to as a result of the withdrawal\\nuint256 updatedTotalShares = priorTotalShares - amountShares;\\n// check to avoid edge case where share rate can be massively inflated as a 'griefing' sort of attack\\nrequire(updatedTotalShares >= MIN\\_NONZERO\\_TOTAL\\_SHARES || updatedTotalShares == 0,\\n    "StrategyBase.withdraw: updated totalShares amount would be nonzero but below MIN\\_NONZERO\\_TOTAL\\_SHARES");\\n```\\n\\nThis particular approach has the downside that, in the worst case, a user may be unable to withdraw the underlying asset for up to 10^9 - 1 shares. While the extreme circumstances under which this can happen might be unlikely to occur in a realistic setting and, in many cases, the value of 10^9 - 1 shares may be negligible, this is not ideal.

Impact:

Recommended mitigation:
It isn't easy to give a good general recommendation. None of the suggested mitigations are without a downside, and what's the best choice may also depend on the specific situation. We do, however, feel that alternative approaches that can't lead to stuck funds might be worth considering, especially for a default implementation.\\nOne option is internal accounting, i.e., the strategy keeps track of the number of underlying tokens it owns. It uses this number for conversion rate calculation instead of its balance in the token contract. This avoids the donation attack because sending tokens directly to the strategy will not affect the conversion rate. Moreover, this technique helps prevent reentrancy issues when the EigenLayer state is out of sync with the token contract's state. The downside is higher gas costs and that donating by just sending tokens to the contract is impossible; more specifically, if it happens accidentally, the funds are lost unless there's some special mechanism to recover them.\\nAn alternative approach with virtual shares and assets is presented here, and the document lists pointers to more discussions and proposed solutions.
Vulnerability: StrategyWrapper - Functions Shouldn't Be virtual (Out of Scope)
Severity: low

Description:
The `StrategyWrapper` contract is a straightforward strategy implementation and - as its NatSpec documentation explicitly states - is not designed to be inherited from:\\n```\\n/\\*\\*\\n \\* @title Extremely simple implementation of `IStrategy` interface.\\n \\* @author Layr Labs, Inc.\\n \\* @notice Simple, basic, "do-nothing" Strategy that holds a single underlying token and returns it on withdrawals.\\n \\* Assumes shares are always 1-to-1 with the underlyingToken.\\n \\* @dev Unlike `StrategyBase`, this contract is \\*not\\* designed to be inherited from.\\n \\* @dev This contract is expressly \\*not\\* intended for use with 'fee-on-transfer'-type tokens.\\n \\* Setting the `underlyingToken` to be a fee-on-transfer token may result in improper accounting.\\n \\*/\\ncontract StrategyWrapper is IStrategy {\\n```\\n\\nHowever, all functions in this contract are `virtual`, which only makes sense if inheriting from `StrategyWrapper` is possible.

Impact:

Recommended mitigation:
Assuming the NatSpec documentation is correct, and no contract should inherit from `StrategyWrapper`, remove the `virtual` keyword from all function definitions. Otherwise, fix the documentation.\\nRemark\\nThis contract is out of scope, and this finding is only included because we noticed it accidentally. This does not mean we have reviewed the contract or other out-of-scope files.
Vulnerability: StrategyManager - Cross-Chain Replay Attacks After Chain Split Due to Hard-Coded DOMAIN_SEPARATOR
Severity: low

Description:
A. The `StrategyManager` contract allows stakers to deposit into and withdraw from strategies. A staker can either deposit themself or have someone else do it on their behalf, where the latter requires an EIP-712-compliant signature. The EIP-712 domain separator is computed in the `initialize` function and stored in a state variable for later retrieval:\\n```\\n/// @notice EIP-712 Domain separator\\nbytes32 public DOMAIN\\_SEPARATOR;\\n```\\n\\n```\\nfunction initialize(address initialOwner, address initialStrategyWhitelister, IPauserRegistry \\_pauserRegistry, uint256 initialPausedStatus, uint256 \\_withdrawalDelayBlocks)\\n    external\\n    initializer\\n{\\n    DOMAIN\\_SEPARATOR = keccak256(abi.encode(DOMAIN\\_TYPEHASH, bytes("EigenLayer"), block.chainid, address(this)));\\n```\\n\\nOnce set in the `initialize` function, the value can't be changed anymore. In particular, the chain ID is “baked into” the `DOMAIN_SEPARATOR` during initialization. However, it is not necessarily constant: In the event of a chain split, only one of the resulting chains gets to keep the original chain ID, and the other should use a new one. With the current approach to compute the `DOMAIN_SEPARATOR` during initialization, store it, and then use the stored value for signature verification, a signature will be valid on both chains after a split - but it should not be valid on the chain with the new ID. Hence, the domain separator should be computed dynamically.\\nB. The `name` in the `EIP712Domain` is of type string:\\n```\\nbytes32 public constant DOMAIN\\_TYPEHASH =\\n    keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");\\n```\\n\\nWhat's encoded when the domain separator is computed is bytes("EigenLayer"):\\n```\\nDOMAIN\\_SEPARATOR = keccak256(abi.encode(DOMAIN\\_TYPEHASH, bytes("EigenLayer"), block.chainid, address(this)));\\n```\\n\\nAccording to EIP-712,\\nThe dynamic values `bytes` and `string` are encoded as a `keccak256` hash of their contents.\\nHence, `bytes("EigenLayer")` should be replaced with `keccak256(bytes("EigenLayer"))`.\\nC. The `EIP712Domain` does not include a version string:\\n```\\nbytes32 public constant DOMAIN\\_TYPEHASH =\\n    keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");\\n```\\n\\nThat is allowed according to the specification. However, given that most, if not all, projects, as well as OpenZeppelin's EIP-712 implementation, do include a version string in their `EIP712Domain`, it might be a pragmatic choice to do the same, perhaps to avoid potential incompatibilities.

Impact:

Recommended mitigation:
Individual recommendations have been given above. Alternatively, you might want to utilize OpenZeppelin's `EIP712Upgradeable` library, which will take care of these issues. Note that some of these changes will break existing signatures.
Vulnerability: StrategyManagerStorage - Miscalculated Gap Size
Severity: low

Description:
Upgradeable contracts should have a “gap” of unused storage slots at the end to allow for adding state variables when the contract is upgraded. The convention is to have a gap whose size adds up to 50 with the used slots at the beginning of the contract's storage.\\nIn `StrategyManagerStorage`, the number of consecutively used storage slots is 10:\\n`DOMAIN_SEPARATOR`\\n`nonces`\\n`strategyWhitelister`\\n`withdrawalDelayBlocks`\\n`stakerStrategyShares`\\n`stakerStrategyList`\\n`withdrawalRootPending`\\n`numWithdrawalsQueued`\\n`strategyIsWhitelistedForDeposit`\\n`beaconChainETHSharesToDecrementOnWithdrawal`\\nHowever, the gap size in the storage contract is 41:\\n```\\nuint256[41] private \\_\\_gap;\\n```\\n

Impact:

Recommended mitigation:
If you don't have to maintain compatibility with an existing deployment, we recommend reducing the storage gap size to 40. Otherwise, we recommend adding a comment explaining that, in this particular case, the gap size and the used storage slots should add up to 51 instead of 50 and that this invariant has to be maintained in future versions of this contract.
Vulnerability: NativeOptimismImpl - Events Will Not Be Emitted in Case of Non-Native Tokens Bridging
Severity: medium

Description:
In the case of the usage of non-native tokens by users, the `SocketBridge` event will not be emitted since the code will return early.\\n```\\nfunction bridgeAfterSwap(\\n```\\n\\n```\\nfunction swapAndBridge(\\n```\\n\\n```\\nfunction bridgeERC20To(\\n```\\n

Impact:

Recommended mitigation:
Make sure that the `SocketBridge` event is emitted for non-native tokens as well.
Vulnerability: Inconsistent Comments
Severity: low

Description:
Some of the contracts in the code have incorrect developer comments annotated for them. This could create confusion for future readers of this code that may be trying to maintain, audit, update, fork, integrate it, and so on.\\n```\\n/\\*\\*\\n \\* @notice function to bridge tokens after swap. This is used after swap function call\\n \\* @notice This method is payable because the caller is doing token transfer and briding operation\\n \\* @dev for usage, refer to controller implementations\\n \\* encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\\n \\* @param swapId routeId for the swapImpl\\n \\* @param swapData encoded data for swap\\n \\* @param stargateBridgeData encoded data for StargateBridgeData\\n \\*/\\nfunction swapAndBridge(\\n```\\n\\nThis is the same comment as `bridgeAfterSwap`, whereas it instead does swapping and bridging together\\n```\\n/\\*\\*\\n \\* @notice function to store the transferId and message-sender of a bridging activity\\n \\* @notice This method is payable because the caller is doing token transfer and briding operation\\n \\* @dev for usage, refer to controller implementations\\n \\* encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\\n \\* @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\\n \\* @param transferIdAddress message sender who is making the bridging on CelerBridge\\n \\*/\\nfunction setAddressForTransferId(\\n```\\n\\nThis comment refers to a payable property of this function when it isn't.\\n```\\n/\\*\\*\\n \\* @notice function to store the transferId and message-sender of a bridging activity\\n \\* @notice This method is payable because the caller is doing token transfer and briding operation\\n \\* @dev for usage, refer to controller implementations\\n \\* encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\\n \\* @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\\n \\*/\\nfunction deleteTransferId(bytes32 transferId) external {\\n```\\n\\nThis comment is copied from the above function when it does the opposite of storing - it deletes the `transferId`

Impact:

Recommended mitigation:
Adjust comments so they reflect what the functions are actually doing.
Vulnerability: Unused Error Codes.
Severity: low

Description:
`error RouteAlreadyExist();`\\n`error ContractContainsNoCode();`\\n`error ControllerAlreadyExist();`\\n`error ControllerAddressIsZero();`\\nIt seems that they were created as errors that may have been expected to occur during the early stages of development, but the resulting architecture doesn't seem to have a place for them currently.\\n```\\nerror RouteAlreadyExist();\\nerror SwapFailed();\\nerror UnsupportedInterfaceId();\\nerror ContractContainsNoCode();\\nerror InvalidCelerRefund();\\nerror CelerAlreadyRefunded();\\nerror ControllerAlreadyExist();\\nerror ControllerAddressIsZero();\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nRemediated as per the client team in SocketDotTech/socket-ll-contracts#148.\\nConsider revisiting these errors and identifying whether they need to remain or can be removed.
Vulnerability: Inaccurate Interface.
Severity: low

Description:
`ISocketGateway` implies a `bridge(uint32 routeId, bytes memory data)` function, but there is no socket contract with a function like that, including the `SocketGateway` contract.\\n```\\nfunction bridge(\\n    uint32 routeId,\\n    bytes memory data\\n) external payable returns (bytes memory);\\n```\\n

Impact:

Recommended mitigation:
Adjust the interface.
Vulnerability: Validate Array Length Matching Before Execution to Avoid Reverts
Severity: low

Description:
The Socket system not only aggregates different solutions via its routes and controllers but also allows to batch calls between them into one transaction. For example, a user may call swaps between several DEXs and then perform a bridge transfer.\\nAs a result, the `SocketGateway` contract has many functions that accept multiple arrays that contain the necessary data for execution in their respective routes. However, these arrays need to be of the same length because individual elements in the arrays are intended to be matched at the same indices:\\n```\\nfunction executeRoutes(\\n    uint32[] calldata routeIds,\\n    bytes[] calldata dataItems,\\n    bytes[] calldata eventDataItems\\n) external payable {\\n    uint256 routeIdslength = routeIds.length;\\n    for (uint256 index = 0; index < routeIdslength; ) {\\n        (bool success, bytes memory result) = addressAt(routeIds[index])\\n            .delegatecall(dataItems[index]);\\n\\n        if (!success) {\\n            assembly {\\n                revert(add(result, 32), mload(result))\\n            }\\n        }\\n\\n        emit SocketRouteExecuted(routeIds[index], eventDataItems[index]);\\n\\n        unchecked {\\n            ++index;\\n        }\\n    }\\n}\\n```\\n\\nNote that in the above example function, all 3 different calldata arrays `routeIds`, `dataItems`, and `eventDataItems` were utilizing the same `index` to retrieve the correct element. A common practice in such cases is to confirm that the sizes of the arrays match before continuing with the execution of the rest of the transaction to avoid costly reverts that could happen due to “Index out of bounds” error.\\nDue to the aggregating and batching nature of the Socket system that may have its users rely on 3rd party offchain APIs to construct these array payloads, such as from APIs of the systems that Socket is integrating, a mishap in just any one of them could cause this issue.

Impact:

Recommended mitigation:
Implement a check on the array lengths so they match.
Vulnerability: Destroyed Routes Eth Balances Will Be Left Locked in SocketDeployFactory
Severity: low

Description:
`SocketDeployFactory.destroy` calls the `killme` function which in turn self-destructs the route and sends back any eth to the factory contract. However, these funds can not be claimed from the `SocketDeployFactory` contract.\\n```\\nfunction destroy(uint256 routeId) external onlyDisabler {\\n```\\n

Impact:

Recommended mitigation:
Make sure that these funds can be claimed.
Vulnerability: RocketMinipoolDelegate - Sandwiching of Minipool calls can have unintended side effects
Severity: high

Description:
The `RocketMinipoolBase` contract exposes the functions `delegateUpgrade` and `delegateRollback`, allowing the minipool owner to switch between delegate implementations. While giving the minipool owner a chance to roll back potentially malfunctioning upgrades, the fact that upgrades and rollback are instantaneous also gives them a chance to alternate between executing old and new code (e.g. by utilizing callbacks) and sandwich user calls to the minipool.\\nAssuming the latest minipool delegate implementation, any user can call `RocketMinipoolDelegate.slash`, which slashes the node operator's RPL balance if a slashing has been recorded on their validator. To mark the minipool as having been `slashed`, the `slashed` contract variable is set to `true`. A minipool owner can avoid this flag from being set By sandwiching the user calls:\\nIn detail, the new slash implementation:\\n```\\nfunction \\_slash() private {\\n    // Get contracts\\n    RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(getContractAddress("rocketNodeStaking"));\\n    // Slash required amount and reset storage value\\n    uint256 slashAmount = nodeSlashBalance;\\n    nodeSlashBalance = 0;\\n    rocketNodeStaking.slashRPL(nodeAddress, slashAmount);\\n    // Record slashing\\n    slashed = true;\\n}\\n```\\n\\nCompared to the old slash implementation:\\n```\\nfunction \\_slash() private {\\n    // Get contracts\\n    RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(getContractAddress("rocketNodeStaking"));\\n    // Slash required amount and reset storage value\\n    uint256 slashAmount = nodeSlashBalance;\\n    nodeSlashBalance = 0;\\n    rocketNodeStaking.slashRPL(nodeAddress, slashAmount);\\n}\\n```\\n\\nWhile the bypass of `slashed` being set is a benign example, the effects of this issue, in general, could result in a significant disruption of minipool operations and potentially affect the system's funds. The impact highly depends on the changes introduced by future minipool upgrades.

Impact:

Recommended mitigation:
We recommend limiting upgrades and rollbacks to prevent minipool owners from switching implementations with an immediate effect. A time lock can fulfill this purpose when a minipool owner announces an upgrade to be done at a specific block. A warning can precede user-made calls that an upgrade is pending, and their interaction can have unintended side effects.
Vulnerability: RocketDAONodeTrustedActions - No way to access ETH provided by non-member votes  Acknowledged
Severity: high

Description:
DAO members can challenge nodes to prove liveliness for free. Non-DAO members must provide `members.challenge.cost = 1 eth` to start a challenge. However, the provided challenge cost is locked within the contract instead of being returned or recycled as system collateral.\\n```\\n// In the event that the majority/all of members go offline permanently and no more proposals could be passed, a current member or a regular node can 'challenge' a DAO members node to respond\\n// If it does not respond in the given window, it can be removed as a member. The one who removes the member after the challenge isn't met, must be another node other than the proposer to provide some oversight\\n// This should only be used in an emergency situation to recover the DAO. Members that need removing when consensus is still viable, should be done via the 'kick' method.\\nfunction actionChallengeMake(address \\_nodeAddress) override external onlyTrustedNode(\\_nodeAddress) onlyRegisteredNode(msg.sender) onlyLatestContract("rocketDAONodeTrustedActions", address(this)) payable {\\n    // Load contracts\\n    RocketDAONodeTrustedInterface rocketDAONode = RocketDAONodeTrustedInterface(getContractAddress("rocketDAONodeTrusted"));\\n    RocketDAONodeTrustedSettingsMembersInterface rocketDAONodeTrustedSettingsMembers = RocketDAONodeTrustedSettingsMembersInterface(getContractAddress("rocketDAONodeTrustedSettingsMembers"));\\n    // Members can challenge other members for free, but for a regular bonded node to challenge a DAO member, requires non-refundable payment to prevent spamming\\n    if(rocketDAONode.getMemberIsValid(msg.sender) != true) require(msg.value == rocketDAONodeTrustedSettingsMembers.getChallengeCost(), "Non DAO members must pay ETH to challenge a members node");\\n    // Can't challenge yourself duh\\n    require(msg.sender != \\_nodeAddress, "You cannot challenge yourself");\\n    // Is this member already being challenged?\\n```\\n

Impact:

Recommended mitigation:
We recommend locking the ETH inside the contract during the challenge process. If a challenge is refuted, we recommend feeding the locked value back into the system as protocol collateral. If the challenge succeeds and the node is kicked, it is assumed that the challenger will be repaid the amount they had to lock up to prove non-liveliness.