msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Assimilators should implement an interface
Severity: high

Description:
The Assimilators are one of the core components within the application. They are used to move the tokens and can be thought of as a “middleware” between the Shell Protocol application and any other supported tokens.\\nThe methods attached to the assimilators are called throughout the application and they are a critical component of the whole system. Because of this fact, it is extremely important that they behave correctly.\\nA suggestion to restrict the possibility of errors when implementing them and when using them is to make all of the assimilators implement a unique specific interface. This way, any deviation would be immediately observed, right when the compilation happens.\\nConsider this example. The user calls `swapByOrigin`.\\n```\\nfunction swapByOrigin (address \\_o, address \\_t, uint256 \\_oAmt, uint256 \\_mTAmt, uint256 \\_dline) public notFrozen returns (uint256 tAmt\\_) {\\n\\n    return transferByOrigin(\\_o, \\_t, \\_dline, \\_mTAmt, \\_oAmt, msg.sender);\\n\\n}\\n```\\n\\nWhich calls `transferByOrigin`. In `transferByOrigin`, if the origin index matches the target index, a different execution branch is activated.\\n```\\nif (\\_o.ix == \\_t.ix) return \\_t.addr.outputNumeraire(\\_rcpnt, \\_o.addr.intakeRaw(\\_oAmt));\\n```\\n\\nIn this case we need the output of `_o.addr.intakeRaw(_oAmt)`.\\nIf we pick a random assimilator and check the implementation, we see the function `intakeRaw` needs to return the transferred amount.\\n```\\n// takes raw cdai amount, transfers it in, calculates corresponding numeraire amount and returns it\\nfunction intakeRaw (uint256 \\_amount) public returns (int128 amount\\_) {\\n\\n    bool success = cdai.transferFrom(msg.sender, address(this), \\_amount);\\n\\n    if (!success) revert("CDai/transferFrom-failed");\\n\\n    uint256 \\_rate = cdai.exchangeRateStored();\\n\\n    \\_amount = ( \\_amount \\* \\_rate ) / 1e18;\\n\\n    cdai.redeemUnderlying(\\_amount);\\n\\n    amount\\_ = \\_amount.divu(1e18);\\n\\n}\\n```\\n\\nHowever, with other implementations, the returns do not match. In the case of `MainnetDaiToDaiAssimilator`, it returns 2 values, which will make the `Loihi` contract work in this case but can misbehave in other cases, or even fail.\\n```\\n// transfers raw amonut of dai in, wraps it in cDai, returns numeraire amount\\nfunction intakeRaw (uint256 \\_amount) public returns (int128 amount\\_, int128 balance\\_) {\\n\\n    dai.transferFrom(msg.sender, address(this), \\_amount);\\n\\n    amount\\_ = \\_amount.divu(1e18);\\n\\n}\\n```\\n\\nMaking all the assimilators implement one unique interface will enforce the functions to look the same from the outside.

Impact:

Recommended mitigation:
Create a unique interface for the assimilators and make all the contracts implement that interface.
Vulnerability: Assimilators do not conform to the ERC20 specification
Severity: medium

Description:
The assimilators in the codebase make heavy usage of both the `transfer` and `transferFrom` methods in the ERC20 standard.\\nQuoting the relevant parts of the specification of the standard:\\nTransfers _value amount of tokens to address _to, and MUST fire the Transfer event. The function SHOULD throw if the message caller's account balance does not have enough tokens to spend.\\nThe transferFrom method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD throw unless the _from account has deliberately authorized the sender of the message via some mechanism.\\nWe can see that, even though it is suggested that ERC20-compliant tokens do `throw` on the lack of authorization from the sender or lack of funds to complete the transfer, the standard does not enforce it.\\nThis means that, in order to make the system both more resilient and future-proof, code in each implementation of current and future assimilators should check for the return value of both `transfer` and `transferFrom` call instead of just relying on the external contract to revert execution.\\nThe extent of this issue is only mitigated by the fact that new assets are only added by the shell administrator and could, therefore, be audited prior to their addition.\\nNon-exhaustive Examples\\n```\\ndai.transferFrom(msg.sender, address(this), \\_amount);\\n```\\n\\n```\\ndai.transfer(\\_dst, \\_amount);\\n```\\n

Impact:

Recommended mitigation:
Add a check for the return boolean of the function.\\nExample:\\n`require(dai.transferFrom(msg.sender, address(this), _amount) == true);`
Vulnerability: Access to assimilators does not check for existence and allows delegation to the zeroth address
Severity: medium

Description:
For every method that allows to selectively withdraw, deposit, or swap tokens in `Loihi`, the user is allowed to specify addresses for the assimilators of said tokens (by inputting the addresses of the tokens themselves).\\nThe shell then performs a lookup on a mapping called `assimilators` inside its main structure and uses the result of that lookup to delegate call the assimilator deployed by the shell administrator.\\nHowever, there are no checks for prior instantiation of a specific, supported token, effectively meaning that we can do a lookup on an all-zeroed-out member of that mapping and delegate call execution to the zeroth address.\\nFor example, the 32 bytes expected as a result of this call:\\n```\\nfunction viewNumeraireAmount (address \\_assim, uint256 \\_amt) internal returns (int128 amt\\_) {\\n\\n    // amount\\_ = IAssimilator(\\_assim).viewNumeraireAmount(\\_amt); // for production\\n\\n    bytes memory data = abi.encodeWithSelector(iAsmltr.viewNumeraireAmount.selector, \\_amt); // for development\\n\\n    amt\\_ = abi.decode(\\_assim.delegate(data), (int128)); // for development\\n\\n}\\n```\\n\\nThis is definitely an insufficient check since the interface for the assimilators might change in the future to include functions that have no return values.

Impact:

Recommended mitigation:
Check for the prior instantiation of assimilators by including the following requirement:\\n`require(shell.assimilators[<TOKEN_ADDRESS>].ix != 0);`\\nIn all the functions that access the `assimilators` mapping and change the indexes to be 1-based instead pf 0-based.
Vulnerability: Math library's fork has problematic changes
Severity: medium

Description:
The math library ABDK Libraries for Solidity was forked and modified to add a few `unsafe_*` functions.\\n`unsafe_add`\\n`unsafe_sub`\\n`unsafe_mul`\\n`unsafe_div`\\n`unsafe_abs`\\nThe problem which was introduced is that `unsafe_add` ironically is not really unsafe, it is as safe as the original `add` function. It is, in fact, identical to the safe `add` function.\\n```\\n/\\*\\*\\n \\* Calculate x + y. Revert on overflow.\\n \\*\\n \\* @param x signed 64.64-bit fixed point number\\n \\* @param y signed 64.64-bit fixed point number\\n \\* @return signed 64.64-bit fixed point number\\n \\*/\\nfunction add (int128 x, int128 y) internal pure returns (int128) {\\n  int256 result = int256(x) + y;\\n  require (result >= MIN\\_64x64 && result <= MAX\\_64x64);\\n  return int128 (result);\\n}\\n```\\n\\n```\\n/\\*\\*\\n \\* Calculate x + y. Revert on overflow.\\n \\*\\n \\* @param x signed 64.64-bit fixed point number\\n \\* @param y signed 64.64-bit fixed point number\\n \\* @return signed 64.64-bit fixed point number\\n \\*/\\nfunction unsafe\\_add (int128 x, int128 y) internal pure returns (int128) {\\n  int256 result = int256(x) + y;\\n  require (result >= MIN\\_64x64 && result <= MAX\\_64x64);\\n  return int128 (result);\\n}\\n```\\n\\nFortunately, `unsafe_add` is not used anywhere in the code.\\nHowever, `unsafe_abs` was changed from this:\\n```\\n/\\*\\*\\n \\* Calculate |x|. Revert on overflow.\\n \\*\\n \\* @param x signed 64.64-bit fixed point number\\n \\* @return signed 64.64-bit fixed point number\\n \\*/\\nfunction abs (int128 x) internal pure returns (int128) {\\n  require (x != MIN\\_64x64);\\n  return x < 0 ? -x : x;\\n}\\n```\\n\\nTo this:\\n```\\n/\\*\\*\\n \\* Calculate |x|. Revert on overflow.\\n \\*\\n \\* @param x signed 64.64-bit fixed point number\\n \\* @return signed 64.64-bit fixed point number\\n \\*/\\nfunction unsafe\\_abs (int128 x) internal pure returns (int128) {\\n  return x < 0 ? -x : x;\\n}\\n```\\n\\nThe check that was removed, is actually an important check:\\n```\\nrequire (x != MIN\\_64x64);\\n```\\n\\n```\\nint128 private constant MIN\\_64x64 = -0x80000000000000000000000000000000;\\n```\\n\\nThe problem is that for an `int128` variable that is equal to `-0x80000000000000000000000000000000`, there is no absolute value within the constraints of `int128`.\\nStarting from int128 `n` = `-0x80000000000000000000000000000000`, the absolute value should be int128 `abs_n` = -n, however `abs_n` is equal to the initial value of `n`. The final value of `abs_n` is still `-0x80000000000000000000000000000000`. It's still not a positive or zero value. The operation `0 - n` wraps back to the same initial value.

Impact:

Recommended mitigation:
Remove unused `unsafe_*` functions and try to find other ways of doing unsafe math (if it is fundamentally important) without changing existing, trusted, already audited code.
Vulnerability: Use one file for each contract or library
Severity: medium

Description:
The repository contains a lot of contracts and libraries that are added in the same file as another contract or library.\\nOrganizing the code in this manner makes it hard to navigate, develop and audit. It is a best practice to have each contract or library in its own file. The file also needs to bear the name of the hosted contract or library.\\n```\\nlibrary SafeERC20Arithmetic {\\n```\\n\\n```\\nlibrary Shells {\\n```\\n\\n```\\ncontract ERC20Approve {\\n    function approve (address spender, uint256 amount) public returns (bool);\\n}\\n```\\n\\n```\\ncontract Loihi is LoihiRoot {\\n```\\n\\n```\\nlibrary Delegate {\\n```\\n\\n```\\nlibrary Assimilators {\\n```\\n

Impact:

Recommended mitigation:
Split up contracts and libraries in single files.
Vulnerability: Remove debugging code from the repository
Severity: medium

Description:
Throughout the repository, there is source code from the development stage that was used for debugging the functionality and was not removed.\\nThis should not be present in the source code and even if they are used while functionality is developed, they should be removed after the functionality was implemented.\\n```\\nevent log(bytes32);\\nevent log\\_int(bytes32, int256);\\nevent log\\_ints(bytes32, int256[]);\\nevent log\\_uint(bytes32, uint256);\\nevent log\\_uints(bytes32, uint256[]);\\n```\\n\\n```\\nevent log(bytes32);\\nevent log\\_uint(bytes32, uint256);\\nevent log\\_int(bytes32, int256);\\n```\\n\\n```\\nevent log(bytes32);\\nevent log\\_int(bytes32, int128);\\nevent log\\_int(bytes32, int);\\nevent log\\_uint(bytes32, uint);\\nevent log\\_addr(bytes32, address);\\n```\\n\\n```\\nevent log(bytes32);\\n```\\n\\n```\\nevent log(bytes32);\\nevent log\\_int(bytes32, int256);\\nevent log\\_ints(bytes32, int256[]);\\nevent log\\_uint(bytes32, uint256);\\nevent log\\_uints(bytes32, uint256[]);\\n```\\n\\n```\\nevent log\\_int(bytes32, int);\\nevent log\\_ints(bytes32, int128[]);\\nevent log\\_uint(bytes32, uint);\\nevent log\\_uints(bytes32, uint[]);\\nevent log\\_addrs(bytes32, address[]);\\n```\\n\\n```\\nevent log\\_uint(bytes32, uint256);\\nevent log\\_int(bytes32, int256);\\n```\\n\\n```\\nevent log\\_uint(bytes32, uint256);\\n```\\n\\n```\\nshell.testHalts = true;\\n```\\n\\n```\\nfunction setTestHalts (bool \\_testOrNotToTest) public {\\n\\n    shell.testHalts = \\_testOrNotToTest;\\n\\n}\\n```\\n\\n```\\nbool testHalts;\\n```\\n

Impact:

Recommended mitigation:
Remove the debug functionality at the end of the development cycle of each functionality.
Vulnerability: Should check if the asset already exists when adding a new asset
Severity: medium

Description:
The public function `includeAsset`\\n```\\nfunction includeAsset (address \\_numeraire, address \\_nAssim, address \\_reserve, address \\_rAssim, uint256 \\_weight) public onlyOwner {\\n    shell.includeAsset(\\_numeraire, \\_nAssim, \\_reserve, \\_rAssim, \\_weight);\\n}\\n```\\n\\nCalls the internal `includeAsset` implementation\\n```\\nfunction includeAsset (Shells.Shell storage shell, address \\_numeraire, address \\_numeraireAssim, address \\_reserve, address \\_reserveAssim, uint256 \\_weight) internal {\\n```\\n\\nBut there is no check to see if the asset already exists in the list. Because the check was not done, `shell.numeraires` can contain multiple identical instances.\\n```\\nshell.numeraires.push(\\_numeraireAssimilator);\\n```\\n

Impact:

Recommended mitigation:
Check if the `_numeraire` already exists before invoking `includeAsset`.
Vulnerability: Check return values for both internal and external calls
Severity: low

Description:
There are some cases where functions which return values are called throughout the source code but the return values are not processed, nor checked.\\nThe returns should in principle be handled and checked for validity to provide more robustness to the code.\\nThe function `intakeNumeraire` receives a number of tokens and returns how many tokens were transferred to the contract.\\n```\\n// transfers numeraire amount of dai in, wraps it in cDai, returns raw amount\\nfunction intakeNumeraire (int128 \\_amount) public returns (uint256 amount\\_) {\\n\\n    // truncate stray decimals caused by conversion\\n    amount\\_ = \\_amount.mulu(1e18) / 1e3 \\* 1e3;\\n\\n    dai.transferFrom(msg.sender, address(this), amount\\_);\\n\\n}\\n```\\n\\nSimilarly, the function `outputNumeraire` receives a destination address and an amount of token for withdrawal and returns a number of transferred tokens to the specified address.\\n```\\n// takes numeraire amount of dai, unwraps corresponding amount of cDai, transfers that out, returns numeraire amount\\nfunction outputNumeraire (address \\_dst, int128 \\_amount) public returns (uint256 amount\\_) {\\n\\n    amount\\_ = \\_amount.mulu(1e18);\\n\\n    dai.transfer(\\_dst, amount\\_);\\n\\n    return amount\\_;\\n\\n}\\n```\\n\\nHowever, the results are not handled in the main contract.\\n```\\nshell.numeraires[i].addr.intakeNumeraire(\\_shells.mul(shell.weights[i]));\\n```\\n\\n```\\nshell.numeraires[i].addr.intakeNumeraire(\\_oBals[i].mul(\\_multiplier));\\n```\\n\\n```\\nshell.reserves[i].addr.outputNumeraire(msg.sender, \\_oBals[i].mul(\\_multiplier));\\n```\\n\\nA sanity check can be done to make sure that more than 0 tokens were transferred to the contract.\\n```\\nunit intakeAmount = shell.numeraires[i].addr.intakeNumeraire(\\_shells.mul(shell.weights[i]));\\nrequire(intakeAmount > 0, "Must intake a positive number of tokens");\\n```\\n

Impact:

Recommended mitigation:
Handle all return values everywhere returns exist and add checks to make sure an expected value was returned.\\nIf the return values are never used, consider not returning them at all.
Vulnerability: Interfaces do not need to be implemented for the compiler to access their selectors.
Severity: low

Description:
```\\nIAssimilator constant iAsmltr = IAssimilator(address(0));\\n```\\n\\nThis pattern is unneeded since you can reference selectors by using the imported interface directly without any implementation. It hinders both gas costs and readability of the code.

Impact:

Recommended mitigation:
```\\nbytes memory data = abi.encodeWithSelector(iAsmltr.viewNumeraireAmount.selector, \\_amt); // for development\\n```\\n\\nuse the expression:\\n`IAssimilator.viewRawAmount.selector`
Vulnerability: Use consistent interfaces for functions in the same group
Severity: low

Description:
This library has 2 functions.\\n`add` which receives 2 arguments, `x` and `y`.\\n```\\nfunction add(uint x, uint y) internal pure returns (uint z) {\\n    require((z = x + y) >= x, "add-overflow");\\n}\\n```\\n\\n`sub` which receives 3 arguments `x`, `y` and `_errorMessage`.\\n```\\nfunction sub(uint x, uint y, string memory \\_errorMessage) internal pure returns (uint z) {\\n    require((z = x - y) <= x, \\_errorMessage);\\n}\\n```\\n\\nIn order to reduce the cognitive load on the auditors and developers alike, somehow-related functions should have coherent logic and interfaces. Both of the functions either need to have 2 arguments, with an implied error message passed to `require`, or both functions need to have 3 arguments, with an error message that can be specified.

Impact:

Recommended mitigation:
Update the functions to be coherent with other related functions.
Vulnerability: Consider emitting an event when changing the frozen state of the contract
Severity: low

Description:
The function `freeze` allows the owner to `freeze` and unfreeze the contract.\\n```\\nfunction freeze (bool \\_freeze) public onlyOwner {\\n    frozen = \\_freeze;\\n}\\n```\\n\\nThe common pattern when doing actions important for the outside of the blockchain is to emit an event when the action is successful.\\nIt's probably a good idea to emit an event stating the contract was frozen or unfrozen.

Impact:

Recommended mitigation:
Create an event that displays the current state of the contract.\\n```\\nevent Frozen(bool frozen);\\n```\\n\\nAnd emit the event when `frozen` is called.\\n```\\nfunction freeze (bool \\_freeze) public onlyOwner {\\n    frozen = \\_freeze;\\n    emit Frozen(\\_freeze);\\n}\\n```\\n
Vulnerability: Function supportsInterface can be restricted to pure
Severity: low

Description:
The function `supportsInterface` returns a `bool` stating that the contract supports one of the defined interfaces.\\n```\\nfunction supportsInterface (bytes4 interfaceID) public returns (bool) {\\n    return interfaceID == ERC20ID || interfaceID == ERC165ID;\\n}\\n```\\n\\nThe function does not access or change the state of the contract, this is why it can be restricted to `pure`.

Impact:

Recommended mitigation:
Restrict the function definition to `pure`.\\n```\\nfunction supportsInterface (bytes4 interfaceID) public pure returns (bool) {\\n```\\n
Vulnerability: Use more consistent function naming (includeAssimilator / excludeAdapter)
Severity: low

Description:
The function `includeAssimilator` adds a new assimilator to the list\\n```\\nshell.assimilators[\\_derivative] = Assimilators.Assimilator(\\_assimilator, \\_numeraireAssim.ix);\\n```\\n\\nThe function `excludeAdapter` removes the specified assimilator from the list\\n```\\ndelete shell.assimilators[\\_assimilator];\\n```\\n

Impact:

Recommended mitigation:
Consider renaming the function `excludeAdapter` to `removeAssimilator` and moving the logic of adding and removing in the same source file.
Vulnerability: Eliminate assembly code by using ABI decode
Severity: high

Description:
There are several locations where assembly code is used to access and decode byte arrays (including uses inside loops). Even though assembly code was used for gas optimization, it reduces the readability (and future updatability) of the code.\\n```\\nassembly {\\n    flag := mload(add(\\_data, 32))\\n}\\nif (flag == CHANGE\\_PARTITION\\_FLAG) {\\n    assembly {\\n        toPartition := mload(add(\\_data, 64))\\n```\\n\\n```\\nassembly {\\n    toPartition := mload(add(\\_data, 64))\\n```\\n\\n```\\nfor (uint256 i = 116; i <= \\_operatorData.length; i = i + 32) {\\n    bytes32 temp;\\n    assembly {\\n        temp := mload(add(\\_operatorData, i))\\n    }\\n    proof[index] = temp;\\n    index++;\\n}\\n```\\n

Impact:

Recommended mitigation:
As discussed in the mid-audit meeting, it is a good solution to use ABI decode since all uses of assembly simply access 32-byte chunks of data from user input. This should eliminate all assembly code and make the code significantly more clean. In addition, it might allow for more compact encoding in some cases (for instance, by eliminating or reducing the size of the flags).\\nThis suggestion can be also applied to Merkle Root verifications/calculation code, which can reduce the for loops and complexity of these functions.
Vulnerability: Ignored return value for transferFrom call
Severity: high

Description:
When burning swap tokens the return value of the `transferFrom` call is ignored. Depending on the token's implementation this could allow an attacker to mint an arbitrary amount of Amp tokens.\\nNote that the severity of this issue could have been Critical if Flexa token was any arbitrarily tokens. We quickly verified that Flexa token implementation would revert if the amount exceeds the allowance, however it might not be the case for other token implementations.\\n```\\nswapToken.transferFrom(\\_from, swapTokenGraveyard, amount);\\n```\\n

Impact:

Recommended mitigation:
The code should be changed like this:\\n```\\nrequire(swapToken.transferFrom(_from, swapTokenGraveyard, amount));\\n```\\n
Vulnerability: Potentially insufficient validation for operator transfers
Severity: medium

Description:
For operator transfers, the current validation does not require the sender to be an operator (as long as the transferred value does not exceed the allowance):\\n```\\nrequire(\\n    \\_isOperatorForPartition(\\_partition, msg.sender, \\_from) ||\\n        (\\_value <= \\_allowedByPartition[\\_partition][\\_from][msg.sender]),\\n    EC\\_53\\_INSUFFICIENT\\_ALLOWANCE\\n);\\n```\\n\\nIt is unclear if this is the intention `or` whether the logical `or` should be a logical `and`.

Impact:

Recommended mitigation:
Resolution\\nremoving `operatorTransferByPartition` and simplifying the interfaces to only `tranferByPartition`\\nThis removes the existing tranferByPartition, converting operatorTransferByPartition to it. The reason for this is to make the client interface simpler, where there is one method to transfer by partition, and that method can be called by either a sender wanting to transfer from their own address, or an operator wanting to transfer from a different token holder address. We found that it was redundant to have multiple methods, and the client convenience wasn't worth the confusion.\\nConfirm that the code matches the intention. If so, consider documenting the behavior (for instance, by changing the name of function `operatorTransferByPartition`.
Vulnerability: Potentially missing nonce check  Acknowledged
Severity: medium

Description:
When executing withdrawals in the collateral manager the per-address withdrawal nonce is simply updated without checking that the new nonce is one greater than the previous one (see Examples). It seems like without such a check it might be easy to make mistakes and causing issues with ordering of withdrawals.\\n```\\naddressToWithdrawalNonce[\\_partition][supplier] = withdrawalRootNonce;\\n```\\n\\n```\\naddressToWithdrawalNonce[\\_partition][supplier] = maxWithdrawalRootNonce;\\n```\\n\\n```\\nmaxWithdrawalRootNonce = \\_nonce;\\n```\\n

Impact:

Recommended mitigation:
Consider adding more validation and sanity checks for nonces on per-address withdrawals.