msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: tbtc - Where possible, use constant rather than state variables
Severity: low

Description:
`TBTCSystem` uses a state variable for `pausedDuration`, but this value is never changed.\\n```\\nuint256 pausedDuration = 10 days;\\n```\\n

Impact:

Recommended mitigation:
Consider using the `constant` keyword.
Vulnerability: tbtc - Variable shadowing in TBTCDepositToken constructor
Severity: low

Description:
`TBTCDepositToken` inherits from `DepositFactoryAuthority`, which has a single state variable, `_depositFactory`. This variable is shadowed in the `TBTCDepositToken` constructor.\\n```\\nconstructor(address \\_depositFactory)\\n    ERC721Metadata("tBTC Deopsit Token", "TDT")\\n    DepositFactoryAuthority(\\_depositFactory)\\npublic {\\n    // solium-disable-previous-line no-empty-blocks\\n}\\n```\\n

Impact:

Recommended mitigation:
Rename the parameter or state variable.
Vulnerability: Ether send function remainder handling
Severity: low

Description:
The Ether send function depicted below implements logic to reimburse the sender if an extraneous amount is left in the contract after the disbursement.\\n```\\nfunction sendEth(address payable [] memory \\_to, uint256[] memory \\_value) public restrictedToOwner payable returns (bool \\_success) {\\n    // input validation\\n    require(\\_to.length == \\_value.length);\\n    require(\\_to.length <= 255);\\n\\n    // count values for refunding sender\\n    uint256 beforeValue = msg.value;\\n    uint256 afterValue = 0;\\n\\n    // loop through to addresses and send value\\n    for (uint8 i = 0; i < \\_to.length; i++) {\\n        afterValue = afterValue.add(\\_value[i]);\\n        assert(\\_to[i].send(\\_value[i]));\\n    }\\n\\n    // send back remaining value to sender\\n    uint256 remainingValue = beforeValue.sub(afterValue);\\n    if (remainingValue > 0) {\\n        assert(msg.sender.send(remainingValue));\\n    }\\n    return true;\\n}\\n```\\n\\nIt is also the only place where the `SafeMath` dependency is being used. More specifically to check there was no underflow in the arithmetic adding up the disbursed amounts.\\nHowever, since the individual sends would revert themselves should more Ether than what was available in the balance be specified these protection measures seem unnecessary.\\nNot only the above is true but the current codebase does not allow to take funds locked within the contract out in the off chance someone forced funds into this smart contract (e.g., by self-destructing some other smart contract containing funds into this one).

Impact:

Recommended mitigation:
The easiest way to handle both retiring `SafeMath` and returning locked funds would be to phase out all the intra-function arithmetic and just transferring `address(this).balance` to `msg.sender` at the end of the disbursement. Since all the funds in there are meant to be from the caller of the function this serves the purpose of returning extraneous funds to him well and, adding to that, it allows for some front-running fun if someone “self-destructed” funds to this smart contract by mistake.
Vulnerability: Unneeded type cast of contract type
Severity: low

Description:
The typecast being done on the `address` parameter in the lien below is unneeded.\\n```\\nERC20 token = ERC20(\\_tokenAddress);\\n```\\n

Impact:

Recommended mitigation:
Assign the right type at the function parameter definition like so:\\n```\\n    function sendErc20(ERC20 _tokenAddress, address[] memory _to, uint256[] memory _value) public restrictedToOwner returns (bool _success) {\\n```\\n
Vulnerability: Inadequate use of assert
Severity: low

Description:
The usage of `require` vs `assert` has always been a matter of discussion because of the fine lines distinguishing these transaction-terminating expressions.\\nHowever, the usage of the `assert` syntax in this case is not the most appropriate.\\nBorrowing the explanation from the latest solidity docs (v. https://solidity.readthedocs.io/en/latest/control-structures.html#id4) :\\n```\\nThe assert function should only be used to test for internal errors, and to check invariants. \\n```\\n\\nSince assert-style exceptions (using the `0xfe` opcode) consume all gas available to the call and require-style ones (using the `0xfd` opcode) do not since the Metropolis release when the `REVERT` instruction was added, the usage of `require` in the lines depicted in the examples section would only result in gas savings and the same security assumptions.\\nIn this case, even though the calls are being made to external contracts the supposedly abide to a predefined specification, this is by no means an invariant of the presented system since the component is external to the built system and its integrity cannot be formally verified.\\n```\\nassert(\\_to[i].send(\\_value[i]));\\n```\\n\\n```\\nassert(msg.sender.send(remainingValue));\\n```\\n\\n```\\nassert(token.transferFrom(msg.sender, \\_to[i], \\_value[i]) == true);\\n```\\n

Impact:

Recommended mitigation:
Exchange the `assert` statements for `require` ones.
Vulnerability: uint overflow may lead to stealing funds
Severity: high

Description:
It's possible to create a delegation with a very huge amount which may result in a lot of critically bad malicious usages:\\n```\\nuint holderBalance = SkaleToken(contractManager.getContract("SkaleToken")).balanceOf(holder);\\nuint lockedToDelegate = tokenState.getLockedCount(holder) - tokenState.getPurchasedAmount(holder);\\nrequire(holderBalance >= amount + lockedToDelegate, "Delegator hasn't enough tokens to delegate");\\n```\\n\\n`amount` is passed by a user as a parameter, so if it's close to `uint` max value, `amount` + lockedToDelegate would overflow and this requirement would pass.\\nHaving delegation with an almost infinite amount of tokens can lead to many various attacks on the system up to stealing funds and breaking everything.

Impact:

Recommended mitigation:
Using `SafeMath` everywhere should prevent this and other similar issues. There should be more critical attacks caused by overflows/underflows, so `SafeMath` should be used everywhere in the codebase.
Vulnerability: Holders can burn locked funds
Severity: high

Description:
Skale token is a modified ERC-777 that allows locking some part of the balance. Locking is checked during every transfer:\\n```\\n// Property of the company SKALE Labs inc.---------------------------------\\n        uint locked = \\_getLockedOf(from);\\n        if (locked > 0) {\\n            require(\\_balances[from] >= locked + amount, "Token should be unlocked for transferring");\\n        }\\n//-------------------------------------------------------------------------\\n        \\_balances[from] = \\_balances[from].sub(amount);\\n        \\_balances[to] = \\_balances[to].add(amount);\\n```\\n\\nBut it's not checked during `burn` function and it's possible to “burn” `locked` tokens. Tokens will be burned, but `locked` amount will remain the same. That will result in having more `locked` tokens than the balance which may have very unpredictable behaviour.

Impact:

Recommended mitigation:
Allow burning only unlocked tokens.
Vulnerability: Node can unlink validator
Severity: high

Description:
Validators can link a node address to them by calling `linkNodeAddress` function:\\n```\\nfunction linkNodeAddress(address validatorAddress, address nodeAddress) external allow("DelegationService") {\\n    uint validatorId = getValidatorId(validatorAddress);\\n    require(\\_validatorAddressToId[nodeAddress] == 0, "Validator cannot override node address");\\n    \\_validatorAddressToId[nodeAddress] = validatorId;\\n}\\n\\nfunction unlinkNodeAddress(address validatorAddress, address nodeAddress) external allow("DelegationService") {\\n    uint validatorId = getValidatorId(validatorAddress);\\n    require(\\_validatorAddressToId[nodeAddress] == validatorId, "Validator hasn't permissions to unlink node");\\n    \\_validatorAddressToId[nodeAddress] = 0;\\n}\\n```\\n\\nAfter that, the node has the same rights and is almost indistinguishable from the validator. So the node can even remove validator's address from `_validatorAddressToId` list and take over full control over validator. Additionally, the node can even remove itself by calling `unlinkNodeAddress`, leaving validator with no control at all forever.\\nAlso, even without nodes, a validator can initially call `unlinkNodeAddress` to remove itself.

Impact:

Recommended mitigation:
Linked nodes (and validator) should not be able to unlink validator's address from the `_validatorAddressToId` mapping.
Vulnerability: Unlocking funds after slashing
Severity: high

Description:
The initial funds can be unlocked if 51+% of them are delegated. However if any portion of the funds are slashed, the rest of the funds will not be unlocked at the end of the delegation period.\\n```\\nif (\\_isPurchased[delegationId]) {\\n    address holder = delegation.holder;\\n    \\_totalDelegated[holder] += delegation.amount;\\n    if (\\_totalDelegated[holder] >= \\_purchased[holder]) {\\n        purchasedToUnlocked(holder);\\n    }\\n```\\n

Impact:

Recommended mitigation:
Consider slashed tokens as delegated, or include them in the calculation for process to unlock in `endingDelegatedToUnlocked`
Vulnerability: Bounties and fees should only be locked for the first 3 months
Severity: high

Description:
Bounties are currently locked for the first 3 months after delegation:\\n```\\nskaleBalances.lockBounty(shares[i].holder, timeHelpers.addMonths(delegationStarted, 3));\\n```\\n\\nInstead, they should be locked for the first 3 months after the token launch.

Impact:

Recommended mitigation:
It's better just to forbid any withdrawals for the first 3 months, no need to track it separately for every delegation. This recommendation is mainly to simplify the process.
Vulnerability: getLockedCount is iterating over all history of delegations
Severity: high

Description:
`getLockedCount` is iterating over all delegations of a specific holder and may even change the state of these delegations by calling `getState`.\\n```\\nfunction getLockedCount(address holder) external returns (uint amount) {\\n    amount = 0;\\n    DelegationController delegationController = DelegationController(contractManager.getContract("DelegationController"));\\n    uint[] memory delegationIds = delegationController.getDelegationsByHolder(holder);\\n    for (uint i = 0; i < delegationIds.length; ++i) {\\n        uint id = delegationIds[i];\\n        if (isLocked(getState(id))) {\\n            amount += delegationController.getDelegation(id).amount;\\n        }\\n    }\\n    return amount + getPurchasedAmount(holder) + this.getSlashedAmount(holder);\\n}\\n```\\n\\nThis problem is major because delegations number is growing over time and may even potentially grow more than the gas limit and lock all tokens forever. `getLockedCount` is called during every transfer which makes any token transfer much more expensive than it should be.

Impact:

Recommended mitigation:
Remove iterations over a potentially unlimited amount of tokens. All the necessary data can be precalculated before and `getLockedCount` function can have O(1) complexity.
Vulnerability: Tokens are unlocked only when delegation ends
Severity: high

Description:
After the first 3 months since at least 50% of tokens are delegated, all tokens should be unlocked. In practice, they are only unlocked if at least 50% of tokens, that were bought on the initial launch, are undelegated.\\n```\\nif (\\_isPurchased[delegationId]) {\\n    address holder = delegation.holder;\\n    \\_totalDelegated[holder] += delegation.amount;\\n    if (\\_totalDelegated[holder] >= \\_purchased[holder]) {\\n        purchasedToUnlocked(holder);\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
Implement lock mechanism according to the legal requirement.
Vulnerability: Tokens after delegation should not be unlocked automatically
Severity: high

Description:
When some amount of tokens are delegated to a validator when the delegation period ends, these tokens are unlocked. However these tokens should be added to `_purchased` as they were in that state before their delegation.\\n```\\nif (\\_isPurchased[delegationId]) {\\n    address holder = delegation.holder;\\n    \\_totalDelegated[holder] += delegation.amount;\\n    if (\\_totalDelegated[holder] >= \\_purchased[holder]) {\\n        purchasedToUnlocked(holder);\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
Tokens should only be unlocked if the main legal requirement `(_totalDelegated[holder] >= _purchased[holder])` is satisfied, which in the above case this has not happened.
Vulnerability: Some unlocked tokens can become locked after delegation is rejected
Severity: high

Description:
When some amount of tokens are requested to be delegated to a validator, the validator can reject the request. The previous status of these tokens should be intact and not changed (locked or unlocked).\\nHere the initial status of tokens gets stored and it's either completely `locked` or unlocked:\\n```\\nif (\\_purchased[delegation.holder] > 0) {\\n    \\_isPurchased[delegationId] = true;\\n    if (\\_purchased[delegation.holder] > delegation.amount) {\\n        \\_purchased[delegation.holder] -= delegation.amount;\\n    } else {\\n        \\_purchased[delegation.holder] = 0;\\n    }\\n} else {\\n    \\_isPurchased[delegationId] = false;\\n}\\n```\\n\\nThe problem is that if some amount of these tokens are locked at the time of the request and the rest tokens are unlocked, they will all be considered as locked after the delegation was rejected.\\n```\\nfunction \\_cancel(uint delegationId, DelegationController.Delegation memory delegation) internal returns (State state) {\\n    if (\\_isPurchased[delegationId]) {\\n        state = purchasedProposedToPurchased(delegationId, delegation);\\n    } else {\\n        state = proposedToUnlocked(delegationId);\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
Don't change the status of the rejected tokens.
Vulnerability: Gas limit for bounty and slashing distribution
Severity: high

Description:
After every bounty payment (should be once per month) to a validator, the bounty is distributed to all delegators. In order to do that, there is a `for` loop that iterates over all active delegators and sends their bounty to `SkaleBalances` contract:\\n```\\nfor (uint i = 0; i < shares.length; ++i) {\\n    skaleToken.send(address(skaleBalances), shares[i].amount, abi.encode(shares[i].holder));\\n\\n    uint created = delegationController.getDelegation(shares[i].delegationId).created;\\n    uint delegationStarted = timeHelpers.getNextMonthStartFromDate(created);\\n    skaleBalances.lockBounty(shares[i].holder, timeHelpers.addMonths(delegationStarted, 3));\\n}\\n```\\n\\nThere are also few more loops over all the active delegators. This leads to a huge gas cost of distribution mechanism. A number of active delegators that can be processed before hitting the gas limit is limited and not big enough.\\nThe same issue is with slashing:\\n```\\nfunction slash(uint validatorId, uint amount) external allow("SkaleDKG") {\\n    ValidatorService validatorService = ValidatorService(contractManager.getContract("ValidatorService"));\\n    require(validatorService.validatorExists(validatorId), "Validator does not exist");\\n\\n    Distributor distributor = Distributor(contractManager.getContract("Distributor"));\\n    TokenState tokenState = TokenState(contractManager.getContract("TokenState"));\\n\\n    Distributor.Share[] memory shares = distributor.distributePenalties(validatorId, amount);\\n    for (uint i = 0; i < shares.length; ++i) {\\n        tokenState.slash(shares[i].delegationId, shares[i].amount);\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
The best solution would require major changes to the codebase, but would eventually make it simpler and safer. Instead of distributing and centrally calculating bounty for each delegator during one call it's better to just store all the necessary values, so delegator would be able to calculate the bounty on withdrawal. Amongst the necessary values, there should be history of total delegated amounts per validator during each bounty payment and history of all delegations with durations of their active state.
Vulnerability: Delegations might stuck in non-active validator  Pending
Severity: medium

Description:
If a validator does not get enough funds to run a node (MSR - Minimum staking requirement), all token holders that delegated tokens to the validator cannot switch to a different validator, and might result in funds getting stuck with the nonfunctioning validator for up to 12 months.\\nExample\\n```\\nrequire((validatorNodes.length + 1) \\* msr <= delegationsTotal, "Validator has to meet Minimum Staking Requirement");\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nSkale team acknowledged this issue and will address this in future versions.\\nAllow token holders to withdraw delegation earlier if the validator didn't get enough funds for running nodes.
Vulnerability: Disabled Validators still have delegated funds  Pending
Severity: medium

Description:
The owner of `ValidatorService` contract can enable and disable validators. The issue is that when a validator is disabled, it still has its delegations, and delegated funds will be locked until the end of their delegation period (up to 12 months).\\n```\\nfunction enableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyOwner {\\n    trustedValidators[validatorId] = true;\\n}\\n\\nfunction disableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyOwner {\\n    trustedValidators[validatorId] = false;\\n}\\n```\\n

Impact:

Recommended mitigation:
It might make sense to release all delegations and stop validator's nodes if it's not trusted anymore. However, the rationale behind disabling the validators might be different that what we think, in any case there should be a way to handle this scenario, where the validator is disabled but there are funds delegated to it.
Vulnerability: _endingDelegations list is redundant
Severity: medium

Description:
`_endingDelegations` is a list of delegations that is created for optimisation purposes. But the only place it's used is in `getPurchasedAmount` function, so only a subset of all delegations is going to be updated.\\n```\\nfunction getPurchasedAmount(address holder) public returns (uint amount) {\\n    // check if any delegation was ended\\n    for (uint i = 0; i < \\_endingDelegations[holder].length; ++i) {\\n        getState(\\_endingDelegations[holder][i]);\\n    }\\n    return \\_purchased[holder];\\n```\\n\\nBut `getPurchasedAmount` function is mostly used after iterating over all delegations of the holder.

Impact:

Recommended mitigation:
Resolution\\nIssue is fixed as a part of the major code changes in skalenetwork/skale-manager#92\\nRemove `_endingDelegations` and switch to a mechanism that does not require looping through delegations list of potentially unlimited size.
Vulnerability: Some functions are defined but not implemented
Severity: medium

Description:
There are many functions that are defined but not implemented. They have a revert with a message as not implemented.\\nThis results in complex code and reduces readability. Here is a some of these functions within the scope of this audit:\\n```\\nfunction getAllDelegationRequests() external returns(uint[] memory) {\\n    revert("Not implemented");\\n}\\n\\nfunction getDelegationRequestsForValidator(uint validatorId) external returns (uint[] memory) {\\n    revert("Not implemented");\\n}\\n```\\n

Impact:

Recommended mitigation:
If these functions are needed for this release, they must be implemented. If they are for future plan, it's better to remove the extra code in the smart contracts.
Vulnerability: Users can burn delegated tokens using re-entrancy attack
Severity: high

Description:
When a user burns tokens, the following code is called:\\n```\\n        uint locked = \\_getAndUpdateLockedAmount(from);\\n        if (locked > 0) {\\n            require(\\_balances[from] >= locked.add(amount), "Token should be unlocked for burning");\\n        }\\n//-------------------------------------------------------------------------\\n\\n        \\_callTokensToSend(\\n            operator, from, address(0), amount, data, operatorData\\n        );\\n\\n        // Update state variables\\n        \\_totalSupply = \\_totalSupply.sub(amount);\\n        \\_balances[from] = \\_balances[from].sub(amount);\\n```\\n\\nThere is a callback function right after the check that there are enough unlocked tokens to burn. In this callback, the user can delegate all the tokens right before burning them without breaking the code flow.

Impact:

Recommended mitigation:
Resolution\\nMitigated in skalenetwork/skale-manager#128\\n`_callTokensToSend` should be called before checking for the unlocked amount of tokens, which is better defined as Checks-Effects-Interactions Pattern.
Vulnerability: Slashes do not affect bounty distribution
Severity: high

Description:
When slashes are processed by a holder, only `_delegatedByHolderToValidator` and `_delegatedByHolder` values are reduced. But `_effectiveDelegatedByHolderToValidator` value remains the same. This value is used to distribute bounties amongst delegators. So slashing will not affect that distribution.\\n```\\nuint oldValue = getAndUpdateDelegatedByHolderToValidator(holder, validatorId);\\nif (oldValue > 0) {\\n    uint month = \\_slashes[index].month;\\n    reduce(\\n        \\_delegatedByHolderToValidator[holder][validatorId],\\n        \\_delegatedByHolder[holder],\\n        \\_slashes[index].reducingCoefficient,\\n        month);\\n    slashingSignals[index.sub(begin)].holder = holder;\\n    slashingSignals[index.sub(begin)].penalty = oldValue.sub(getAndUpdateDelegatedByHolderToValidator(holder, validatorId));\\n}\\n```\\n

Impact:

Recommended mitigation:
Reduce `_effectiveDelegatedByHolderToValidator` and `_effectiveDelegatedToValidator` when slashes are processed.
Vulnerability: Storage operations optimization
Severity: medium

Description:
There are a lot of operations that write some value to the storage (uses `SSTORE` opcode) without actually changing it.\\nIn `getAndUpdateValue` function of `DelegationController` and TokenLaunchLocker:\\n```\\nfor (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {\\n    sequence.value = sequence.value.add(sequence.addDiff[i]).sub(sequence.subtractDiff[i]);\\n    delete sequence.addDiff[i];\\n    delete sequence.subtractDiff[i];\\n}\\n```\\n\\nIn `handleSlash` function of `Punisher` contract `amount` will be zero in most cases:\\n```\\nfunction handleSlash(address holder, uint amount) external allow("DelegationController") {\\n    \\_locked[holder] = \\_locked[holder].add(amount);\\n}\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nMitigated in skalenetwork/skale-manager#179\\nCheck if the value is the same and don't write it to the storage in that case.