Yieldy contest - 0x1f8b's results

A protocol for gaining single side yields on various tokens.

General Information

Platform: Code4rena

Start Date: 21/06/2022

Pot Size: $50,000 USDC

Total HM: 31

Participants: 99

Period: 5 days

Judges: moose-code, JasoonS, denhampreen

Total Solo HM: 17

Id: 139

League: ETH

Yieldy

Findings Distribution

Researcher Performance

Rank: 3/99

Findings: 5

Award: $3,436.45

🌟 Selected for report: 3

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: 0x1f8b

Also found by: BowTiedWardens, Lambda, StErMi, berndartmueller, csanuragjain, neumo, rfa

Labels

bug
3 (High Risk)
resolved
sponsor confirmed

Awards

1817.5513 USDC - $1,817.55

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/34774d3f5e9275978621fd20af4fe466d195a88b/src/contracts/BatchRequests.sol#L93 https://github.com/code-423n4/2022-06-yieldy/blob/34774d3f5e9275978621fd20af4fe466d195a88b/src/contracts/BatchRequests.sol#L57 https://github.com/code-423n4/2022-06-yieldy/blob/34774d3f5e9275978621fd20af4fe466d195a88b/src/contracts/BatchRequests.sol#L37

Vulnerability details

Impact

The BatchRequests.removeAddress logic is wrong and it will produce a denial of service.

Proof of Concept

Removing the element from the array is done using the delete statement, but this is not the proper way to remove an entry from an array, it will just set that position to address(0).

Append dummy data:

  • addAddress('0x0000000000000000000000000000000000000001')
  • addAddress('0x0000000000000000000000000000000000000002')
  • addAddress('0x0000000000000000000000000000000000000003')
  • getAddresses() => address[]: 0x0000000000000000000000000000000000000001,0x0000000000000000000000000000000000000002,0x0000000000000000000000000000000000000003

Remove address:

  • removeAddress(0x0000000000000000000000000000000000000002) (or 0x0000000000000000000000000000000000000003)
  • getAddresses() => address[]: 0x0000000000000000000000000000000000000001,0x0000000000000000000000000000000000000000,0x0000000000000000000000000000000000000003

Service is denied because it will try to call canBatchContracts to address(0).

  • To remove an entry in an array you have to use pop and move the last element to the removed entry position.

#0 - toshiSat

2022-06-28T16:32:06Z

duplicate #280

#1 - 0x1f8b

2022-07-01T16:14:02Z

@toshiSat

duplicate https://github.com/code-423n4/2022-06-yieldy-findings/issues/280

Is not the same issue, here I describe a wrong logic during removeAddress

#2 - toshiSat

2022-07-01T17:51:54Z

@toshiSat

duplicate #280

Is not the same issue, here I describe a wrong logic during removeAddress

Sorry! lot's of issues

duplicate #289

#3 - JasoonS

2022-07-29T13:14:21Z

Agree this is high, if the team (owner) didn't know this they could cause some issues for sure.

Findings Information

🌟 Selected for report: 0x1f8b

Also found by: 0xNineDec, StErMi

Labels

bug
2 (Med Risk)
resolved
sponsor confirmed
sponsor disputed

Awards

327.1592 USDC - $327.16

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/8400e637d9259b7917bde259a5a2fbbeb5946d45/src/contracts/Yieldy.sol#L212

Vulnerability details

Impact

The security of the Yieldy contract is delegated to the compiler used.

Proof of Concept

The allowance of an account does not have to reflect the real balance of an account, however in the transferFrom method, it is the value that is checked in order to verify that the user has enough balance to make the transfer.

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) public override returns (bool) {
        require(_allowances[_from][msg.sender] >= _value, "Allowance too low");

However, the real balance of the Yieldy contract is based on the calculation made by the creditsForTokenBalance method, so an underflow could be made in the subtraction of the balance of the from account.

        uint256 creditAmount = creditsForTokenBalance(_value);
        creditBalances[_from] = creditBalances[_from] - creditAmount;
        creditBalances[_to] = creditBalances[_to] + creditAmount;
        emit Transfer(_from, _to, _value);

This means that the security of the contract is delegated to the checks added by the compiler depending on the pragma used, it must be taken into account that these checks may appear and disappear in future versions of the compiler, so they must be checked at the level of smart contracts.

Affected source code:

  • Check that the from account has a creditAmount balance.

#0 - toshiSat

2022-07-29T20:41:19Z

Looking into this, the balance isn't calculated through the creditsForTokenBalance method, it's calculated through the balanceOf method, which in this case the functionality is correct. We aren't transferring credits, we are transferring the value and adding to the credits. Allowance is for value amounts, not credits, also balance can only go up against credits, so if the balance is valid then credits are inherently valid too. I'm unsure of what to label this as, because we do need to check to see if the user has the correct balance. I feel like this issue is partially correct

#1 - toshiSat

2022-07-29T20:45:44Z

Findings Information

🌟 Selected for report: 0x1f8b

Labels

bug
2 (Med Risk)
sponsor acknowledged

Awards

1211.7009 USDC - $1,211.70

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/8400e637d9259b7917bde259a5a2fbbeb5946d45/src/contracts/Yieldy.sol#L268

Vulnerability details

Proof of Concept

Using the burn() function of Yieldy, an address with MINTER_BURNER_ROLE can burn an arbitrary amount of tokens from any address.

We believe this is unnecessary and poses a serious centralization risk.

A malicious or compromised MINTER_BURNER_ROLE address can take advantage of this.

Consider removing the MINTER_BURNER_ROLE and change burn() function to:

    function burn(uint256 _amount) external override 
    {
        _burn(_msgSender(), _amount);
    }

#0 - toshiSat

2022-07-27T22:35:18Z

There's tons of centralization risks already, this is acknowledged, but for yieldies to work, there needs to be a trusted party

#1 - JasoonS

2022-08-29T16:51:36Z

Leaving as medium - the code can be upgraded but the code is being assessed as is.

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/8400e637d9259b7917bde259a5a2fbbeb5946d45/src/contracts/Yieldy.sol#L38 https://github.com/code-423n4/2022-06-yieldy/blob/8400e637d9259b7917bde259a5a2fbbeb5946d45/src/contracts/Yieldy.sol#L61-L62

Vulnerability details

Impact

A deprecated method is used in Yieldy contract.

Proof of Concept

In Yieldy contract the method _setupRole is used, and and it is explicitly marked as deprecated by OpenZeppelin.

  • NOTE: This function is deprecated in favor of {_grantRole}.

Affected source code:

  • The method _setupRole must be changed to _grantRole.

#0 - JasoonS

2022-07-26T13:58:12Z

Low severity - agreed

Reduce math operations

Use scientific notation (e.g. 10e17) rather than exponentiation (e.g. 10**18)

Change 10**3 to 10e2:

Save storage slots

It's possible to save storage slots reorganizing the following contracts:

Reduce the size of error messages (Long revert Strings)

Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.

Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.

I suggest shortening the revert strings to fit in 32 bytes, or that using custom errors as described next (require pragma upgrade).

Use Custom Errors instead of Revert Strings to save Gas

Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)

Source Custom Errors in Solidity:

Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.

Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).

Affected source code:

Avoid use Counter to only increase one variable

The Counter contract is only used to increase a variable and involves unnecessary cost overruns.

Affected source code:

unckecked keyword

It's possible to save gas using the unckecked keyword. This will avoid the required checks to ensure that the variable won't overflow.

Reference:

Affected source code:

Use right type for avoid casting

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