Drips Protocol contest - btk's results

An Ethereum protocol for streaming and splitting funds.

General Information

Platform: Code4rena

Start Date: 25/01/2023

Pot Size: $90,500 USDC

Total HM: 3

Participants: 26

Period: 9 days

Judge: GalloDaSballo

Id: 209

League: ETH

Drips Protocol

Findings Distribution

Researcher Performance

Rank: 24/26

Findings: 1

Award: $122.82

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
grade-b
QA (Quality Assurance)
edited-by-warden
Q-14

Awards

122.8177 USDC - $122.82

External Links

Total Low issues
RiskIssues DetailsNumber
[L-01]The protocol is using draft-EIP712.sol which is deprecated1
[L-02]Integer overflow by unsafe casting11
[L-03]Use safeMint instead of mint for ERC7211
[L-04]Array lengths not checked4
[L-05]Critical changes should use two-step procedure1
[L-06]Loss of precision due to rounding4
[L-07]No storage gap for upgradeable contracts1
[L-08]Unhandled return values of transferFrom1
[L-09]Missing Event for initialize8
[L-10]admin can renounce while system is paused1
Total Non-Critical issues
RiskIssues DetailsNumber
[NC-01]Lock pragmas to specific compiler versionAll Contracts
[NC-02]Contracts should have full test coverageAll Contracts
[NC-03]Constants in comparisons should appear on the left side12
[NC-04]Address(0) checks4
[NC-05]Include @return parameters in NatSpec comments3 Contracts
[NC-06]Function writing does not comply with the Solidity Style GuideAll Contracts
[NC-07]Use bytes.concat() and string.concat()1
[NC-08]For functions, follow Solidity standard naming conventionsAll Contracts
[NC-09]Generate perfect code headers every timeAll Contracts
[NC-10]There is no need to cast a variable that is already an address, such as address(x)2
[NC-11]Consider using delete rather than assigning zero to clear values2
[NC-12]Constants should be defined rather than using magic numbers2
[NC-13]Need Fuzzing test1 Contracts
[NC-14]Assembly Codes Specific – Should Have Comments6
[NC-15]Use SMTChecker
[NC-16]Add NatSpec comment to mapping9
[NC-17]Not using the named return variables anywhere in the function is confusingAll Contracts

[L-01] The protocol is using draft-EIP712.sol which is deprecated

Description

While OpenZeppelin draft contracts are safe to use and have been audited, their 'draft' status means that the EIPs they're based on are not finalized, and thus there may be breaking changes in even minor releases. If a bug is found in this version of OpenZeppelin, and the version that you're forced to upgrade to has breaking changes in the new version, you'll encounter unnecessary delays in porting and testing replacement contracts. Ensure that you have extensive test coverage of this area so that differences can be automatically detected, and have a plan in place for how you would fully test a new version of these contracts if they do indeed change unexpectedly.

// EIP-712 is Final as of 2022-08-11. This file is deprecated.

Refrence

Lines of code

Consider creating a forked version of the file rather than importing it from the package, and manually patch your fork as changes are made.

[L-02] Integer overflow by unsafe casting

Description

Keep in mind that the version of solidity used, despite being greater than 0.8, does not prevent integer overflows during casting, it only does so in mathematical operations.

It is necessary to safely convert between the different numeric types.

        splitAmt = uint128((uint160(amount) * splitsWeight) / _TOTAL_SPLITS_WEIGHT);
Lines of code

Use openzeppelin safeCast.sol library to prevent unexpected overflows when casting from uint256.

[L-03] Use safeMint instead of mint for ERC721

Description

Users could lost their NFTs if msg.sender is a contract address that does not support ERC721, the NFT can be frozen in the contract forever.

As per the documentation of EIP-721:

A wallet/broker/auction application MUST implement the wallet interface if it will accept safe transfers.

Ref: https://eips.ethereum.org/EIPS/eip-721

As per the documentation of ERC721.sol by Openzeppelin:

Ref: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L274-L285

    function mint(address to, UserMetadata[] calldata userMetadata)
        public
        whenNotPaused
        returns (uint256 tokenId)
    {
        tokenId = _registerTokenId();
        _mint(to, tokenId);
        if (userMetadata.length > 0) dripsHub.emitUserMetadata(tokenId, userMetadata);
    }
Lines of code

Use _safeMint instead of mint to check received address support for ERC721 implementation.

[L-04] Array lengths not checked

Description

If the length of the arrays are not required to be of the same length, user operations may not be fully executed.

    function setDrips(
        IERC20 erc20,
        DripsReceiver[] calldata currReceivers,
        int128 balanceDelta,
        DripsReceiver[] calldata newReceivers,
        // slither-disable-next-line similar-names
        uint32 maxEndHint1,
        uint32 maxEndHint2,
        address transferTo
    ) public whenNotPaused returns (int128 realBalanceDelta) {
        // @audit array length
        if (balanceDelta > 0) {
            _transferFromCaller(erc20, uint128(balanceDelta));
        }
        realBalanceDelta = dripsHub.setDrips(
            callerUserId(),
            erc20,
            currReceivers,
            balanceDelta,
            newReceivers,
            maxEndHint1,
            maxEndHint2
        );
        if (realBalanceDelta < 0) {
            // @audit check if transferTo is a valid address
            erc20.safeTransfer(transferTo, uint128(-realBalanceDelta));
        }
    }
Lines of code
if (currReceivers != newReceivers) revert("Array length mismatch");

[L-05] Critical changes should use two-step procedure

Description

The protocol inherit openzeppelin ERC1967Upgrade.sol which does not use two-step procedure when changing the admin, and since the protocol rely heavily on the onlyAdmin() modifier (8 results in 1 files), Thus using two-step procedure is a best practice in case of transferring the admin role to an invalid address.

    function changeAdmin(address newAdmin) public onlyAdmin {
        _changeAdmin(newAdmin);
    }
Lines of code

Consider adding two step procedure on the critical functions where the first is announcing a pendingAdmin and the new address should then claim the admin role.

[L-06] Loss of precision due to rounding

Description

Loss of precision due to the nature of arithmetics and rounding errors.

        splitAmt = uint128((uint160(amount) * splitsWeight) / _TOTAL_SPLITS_WEIGHT);
Lines of code

[L-07] No storage gap for upgradeable contracts

Description

For upgradeable contracts, inheriting contracts may introduce new variables. In order to be able to add new variables to the upgradeable contract without causing storage collisions, a storage gap should be added to the upgradeable contract.

See this for a description of this storage variable.

Lines of code

Consider adding a storage gap at the end of the upgradeable contract:

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;

[L-08] Unhandled return values of transferFrom

Description

ERC20 implementations are not always consistent. Some implementations of transferFrom could return false on failure instead of reverting. It is safer to check the return value and revert on false to prevent these failures.

This is a known issue with solmate and Opanzeppelin ERC20 libraries, For example, Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens, hence Tether (USDT) transferFrom() function do not return boolean as the specification requires, and instead have no return value.

    function transferFrom(address from, address to, uint256 tokenId)
        public
        override
        whenNotPaused
    {
        super.transferFrom(from, to, tokenId);
    }
Lines of code

Check the return value and revert() on false.

[L-09] Missing Event for initialize

Description

Events help non-contract tools to track changes, and events prevent users from being surprised by changes Issuing event-emit during initialization is a detail that many projects skip.

Lines of code

Add Event-Emit

[L-10] admin can renounce while system is paused

Description

The contract admin is not prevented from renouncing the role/ownership while the contract is paused, which would cause any user assets stored in the protocol, to be locked indefinitely.

    function pause() public onlyAdminOrPauser whenNotPaused {
        _managedStorage().isPaused = true;
        emit Paused(msg.sender);
    }
Lines of code

[NC-01] Lock pragmas to specific compiler version

Description

Pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or EthPM package. Otherwise, the developer would need to manually update the pragma in order to compile locally.

Ref: https://swcregistry.io/docs/SWC-103

pragma solidity ^0.8.17;
Lines of code

Ethereum Smart Contract Best Practices: Lock pragmas to specific compiler version.

[NC-02] Contracts should have full test coverage

Description

While 100% code coverage does not guarantee that there are no bugs, it often will catch easy-to-find bugs, and will ensure that there are fewer regressions when the code invariably has to be modified. Furthermore, in order to get full coverage, code authors will often have to re-organize their code so that it is more modular, so that each component can be tested separately, which reduces interdependencies between modules and layers, and makes for code that is easier to reason about and audit.

- What is the overall line coverage percentage provided by your tests?:  90
Lines of code

Line coverage percentage should be 100%.

[NC-03] Constants in comparisons should appear on the left side

Description

Constants in comparisons should appear on the left side, doing so will prevent typo bug.

        if (start == 0) {
            start = updateTime;
        }
Lines of code
        if (0 == start) {
            start = updateTime;
        }

[NC-04] Address(0) checks

Description

Check of address(0) to protect the code from (0x0000000000000000000000000000000000000000) address problem just in case. This is best practice or instead of suggesting that they verify _address != address(0), you could add some good NatSpec comments explaining what is valid and what is invalid and what are the implications of accidentally using an invalid address.

    constructor(DripsHub _dripsHub, address forwarder, uint32 _driverId)
        ERC2771Context(forwarder)
    {
        dripsHub = _dripsHub;
        driverId = _driverId;
    }
Lines of code

Add a check for address(0).

[NC-05] Include @return parameters in NatSpec comments

Description

If Return parameters are declared, you must prefix them with /// @return. Some code analysis programs do analysis by reading NatSpec details, if they can't see the @return tag, they do incomplete analysis.

Lines of code

Include the @return argument in the NatSpec comments.

[NC-06] Function writing does not comply with the Solidity Style Guide

Description

Ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.

Functions should be grouped according to their visibility and ordered:

  • constructor()
  • receive()
  • fallback()
  • external / public / internal / private
  • view / pure
Lines of code

Follow Solidity Style Guide.

[NC-07] Use bytes.concat() and string.concat()

Description
  • bytes.concat() vs abi.encodePacked(<bytes>,<bytes>)
  • string.concat() vs abi.encodePacked(<string>,<string>)

https://docs.soliditylang.org/en/v0.8.17/types.html?highlight=bytes.concat#the-functions-bytes-concat-and-string-concat

        return Address.functionCallWithValue(to, abi.encodePacked(data, sender), value);
Lines of code

Use bytes.concat() and string.concat() instead.

[NC-08] For functions, follow Solidity standard naming conventions

Description

The protocol don't follow solidity standard naming convention.

Ref: https://docs.soliditylang.org/en/v0.8.17/style-guide.html#naming-conventions

Lines of code

Follow solidity standard naming convention.

[NC-09] Generate perfect code headers every time

Description

I recommend using header for Solidity code layout and readability.

Ref: https://github.com/transmissions11/headers

Lines of code
/*//////////////////////////////////////////////////////////////
                           TESTING 123
//////////////////////////////////////////////////////////////*/

[NC-10] There is no need to cast a variable that is already an address, such as address(x)

Description

There is no need to cast a variable that is already an address, such as address(x), x is also address.

       if (erc20.allowance(address(this), address(dripsHub)) == 0) {
            erc20.safeApprove(address(dripsHub), type(uint256).max);
        }
Lines of code
       if (erc20.allowance(address(this), dripsHub) == 0) {
            erc20.safeApprove(dripsHub, type(uint256).max);
        }

[NC-11] Consider using delete rather than assigning zero to clear values

Description

The delete keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic.

        balance.splittable = 0;
Lines of code
        delete balance.splittable;

[NC-12] Constants should be defined rather than using magic numbers

Description
            uint256 idxMid = (idx + idxCap) / 2;
                uint256 end = (enoughEnd + notEnoughEnd) / 2;
Lines of code
uint8 private constant TWO = 2;

[NC-13] Need Fuzzing test

Description

In total 1 contract, 6 unchecked{} are used, the functions used are critical. For this reason, there must be fuzzing tests in the tests of the contract. Not seen in tests.

Lines of code

Use should fuzzing test like Echidna. As Alberto Cuesta Canada said: Fuzzing is not easy, the tools are rough, and the math is hard, but it is worth it. Fuzzing gives me a level of confidence in my smart contracts that I didn’t have before. Relying just on unit testing anymore and poking around in a testnet seems reckless now.

Ref: https://medium.com/coinmonks/smart-contract-fuzzing-d9b88e0b0a05

[NC-14] Assembly Codes Specific – Should Have Comments

Description

Since this is a low level language that is more difficult to parse by readers, include extensive documentation, comments on the rationale behind its use, clearly explaining what each assembly instruction does.

This will make it easier for users to trust the code, for reviewers to validate the code, and for developers to build on or update the code.

Note that using Aseembly removes several important security features of Solidity, which can make the code more insecure and more error-prone.

Lines of code

Include NatSpec in assembly codes.

[NC-15] Use SMTChecker

Description

The highest tier of smart contract behavior assurance is formal mathematical verification. All assertions that are made are guaranteed to be true across all inputs → The quality of your asserts is the quality of your verification.

Ref: https://twitter.com/0xOwenThurm/status/1614359896350425088?t=dbG9gHFigBX85Rv29lOjIQ&s=19

Lines of code

Use SMTChecker

[NC-16] Add NatSpec comment to mapping

Description

Add NatSpec comments describing mapping keys and values.

    mapping(address => uint256) public nonce;
Lines of code
 /// @dev  address(user) => uint256(nonce)
    mapping(address => uint256) public nonce;

[NC-17] Not using the named return variables anywhere in the function is confusing

Description

Not using the named return variables anywhere in the function is confusing.

    function isAuthorized(address sender, address user) public view returns (bool authorized) {
        return _authorized[sender].contains(user);
    }
Lines of code

Consider changing the variable to be an unnamed one.

    function isAuthorized(address sender, address user) public view returns (bool) {
        return _authorized[sender].contains(user);
    }

#0 - GalloDaSballo

2023-02-14T20:23:25Z

[L-01] The protocol is using draft-EIP712.sol which is deprecated 1 R

[L-02] Integer overflow by unsafe casting 11 Disputing for lack of proof

[L-03] Use safeMint instead of mint for ERC721 1 L

[L-04] Array lengths not checked 4 R

[L-05] Critical changes should use two-step procedure 1 NC

[L-06] Loss of precision due to rounding 4 Disputing due to lack of proof

[L-07] No storage gap for upgradeable contracts 1 R

[L-08] Unhandled return values of transferFrom 1 INVALID - 3

[L-09] Missing Event for initialize 8 NC

[L-10] admin can renounce while system is paused 1 NC

[NC-01] Lock pragmas to specific compiler version All Contracts NC

[NC-02] Contracts should have full test coverage All Contracts Disputing

[NC-03] Constants in comparisons should appear on the left side 12 NC

[NC-04] Address(0) checks 4 L

[NC-05] Include @return parameters in NatSpec comments 3 Contracts NC

[NC-06] Function writing does not comply with the Solidity Style Guide All Contracts NC

[NC-07] Use bytes.concat() and string.concat() 1 NC

[NC-08] For functions, follow Solidity standard naming conventions All Contracts NC

[NC-09] Generate perfect code headers every time All Contracts Ignoring

[NC-10] There is no need to cast a variable that is already an address, such as address(x) 2 Invalid

[NC-11] Consider using delete rather than assigning zero to clear values 2 Invalid

[NC-12] Constants should be defined rather than using magic numbers 2 R

[NC-13] Need Fuzzing test 1 Contracts Disputing

[NC-14] Assembly Codes Specific – Should Have Comments 6 Disputing

[NC-15] Use SMTChecker Same

[NC-16] Add NatSpec comment to mapping 9 NC

[NC-17] Not using the named return variables anywhere in the function is confusing R

#1 - GalloDaSballo

2023-02-23T16:16:52Z

2L 5R 10NC - 3

#2 - c4-judge

2023-02-24T10:55:36Z

GalloDaSballo marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter