Frankencoin - descharre's results

A decentralized and fully collateralized stablecoin.

General Information

Platform: Code4rena

Start Date: 12/04/2023

Pot Size: $60,500 USDC

Total HM: 21

Participants: 199

Period: 7 days

Judge: hansfriese

Total Solo HM: 5

Id: 231

League: ETH

Frankencoin

Findings Distribution

Researcher Performance

Rank: 128/199

Findings: 1

Award: $22.60

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Summary

Low Risk

IDFindingInstances
L-01Average block per day is lower than 72004
L-02Minter can skip fees4

Non critical

IDFindingInstances
N-01A year doesn't equal 52 weeks1
N-02Revert should be used instead of require when conition is always false1
N-03Missing 0 address check for sender1
N-04Panic exception 0x11 can occur when burning token1
N-05Bitshifting makes code complicated and is prone to errors1
N-06Using empty super function1

Details

Low Risk

[L-01] Average block per day is lower than 7200

MIN_HOLDING_DURATION is calculated by doing 90*7200. However the average blocks per day is closer to 7100. Which mean the min holding duration will be more than 90 days.

    uint256 public constant MIN_HOLDING_DURATION = 90*7200 << BLOCK_TIME_RESOLUTION_BITS;

[L-02] Minter can skip fees

There are two mint() functions in the Frankencoin contract. They both mint frankencoins, however one has fees and the other one has not. Since there is no disadvantage for the minter to use the mint function without fees he will always use it and skip the fees.

Frankencoin.sol#L165-L174

   function mint(address _target, uint256 _amount, uint32 _reservePPM, uint32 _feesPPM) override external minterOnly {
      uint256 usableMint = (_amount * (1000_000 - _feesPPM - _reservePPM)) / 1000_000; // rounding down is fine
      _mint(_target, usableMint);
      _mint(address(reserve), _amount - usableMint); // rest goes to equity as reserves or as fees
      minterReserveE6 += _amount * _reservePPM; // minter reserve must be kept accurately in order to ensure we can get back to exactly 0
   }

   function mint(address _target, uint256 _amount) override external minterOnly {
      _mint(_target, _amount);
   }

Non critical

[N-01] A year doesn't equal 52 weeks

A year is 52 weeks + 1 day, which means that the date that the contract needs to be updated will be one day earlier every year (or 2 in a leap year). StablecoinBridge.sol#L29

[N-02] Revert should be used instead of require when conition is always false

StablecoinBridge.sol#L81

-    require(false, "unsupported token");
+    revert("unsupported token");

[N-03] Missing 0 address check for sender

ERC20.sol#L151-L159: the comment says sender cannot be the zero address. However there is only a check for the recipient and not for the sender. If this meant to be, the comment should be removed.

[N-04] Panic exception 0x11 can occur when burning token

When burning the token there is no require statement to check if the amount is greater than the account balance. Because of this underflow can happen and will result in the Panic exception 0x11

    function _burn(address account, uint256 amount) internal virtual {
        _beforeTokenTransfer(account, address(0), amount);

        _totalSupply -= amount;
        _balances[account] -= amount;
        emit Transfer(account, address(0), amount);
    }

[N-05] Bitshifting makes code complicated and is prone to errors

While it saves gas to put multiple values in one storage slot using bitshifting, it also makes the code really complicated. Equity.sol#L76

[N-06] Using empty super function

The function _beforeTokenTransfer() in Equity.sol also uses the super function _beforeTokenTransfer(). However this function is empty so it will do nothing. The call to the super function should be removed.

    function _beforeTokenTransfer(address from, address to, uint256 amount) override internal {
        super._beforeTokenTransfer(from, to, amount);
        if (amount > 0){
            // No need to adjust the sender votes. When they send out 10% of their shares, they also lose 10% of
            // their votes so everything falls nicely into place.
            // Recipient votes should stay the same, but grow faster in the future, requiring an adjustment of the anchor.
            uint256 roundingLoss = adjustRecipientVoteAnchor(to, amount);
            // The total also must be adjusted and kept accurate by taking into account the rounding error.
            adjustTotalVotes(from, amount, roundingLoss);
        }
    }

#0 - c4-judge

2023-05-16T02:57:49Z

hansfriese 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