Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · December 2025

USD8

for M0

Guardian's review of USD8 for M0, published December 2025. The report records 10 findings, including 1 high and 1 low.

Published
Review window
November 28 to 29, 2025
Language
Solidity
Chains
Ethereum, Arbitrum, Optimism, Linea, Unichain, Solana
Sector
Stablecoins
  • 0 Critical
  • 1 High
  • 0 Medium
  • 1 Low
  • 8 Informational

6 resolved · 4 acknowledged

Scope

Findings 10

  1. H-01 High Contracts Do Not Compile Typo Resolved
    Location
    USD8.sol

    Description

    In the claimYield function the hasRole check has been replaced with sl which does not compile.

    Recommendation

    Correct the typo.

  2. L-01 Low setYieldRecipient Not Always Callable DoS Resolved
    Location
    USD8.sol

    Description

    MYieldToOne.setYieldRecipient() unconditionally calls claimYield() before updating the recipient. USD8’s claimYield reverts if currentYield == 0 and also reverts when fee > 0 but protocolFeeAmount rounds to 0 (dust), and it is additionally blocked when paused. This means setYieldRecipient will revert under common conditions (no yield yet, small yield, or paused), preventing administrators from changing the yield destination.

    Recommendation

    Consider refactoring this flow to early return in these cases instead of reverting to allow this configuration to occur.

  3. I-01 Informational Outdated Initialize NatSpec Documentation Resolved
    Location
    USD8.sol

    Description

    In the NatSpec for the initialize function there is no NatSpec for the forcedTransferManager parameter.

    Recommendation

    Update the NatSpec for the initialize function to include the forcedTransferManager parameter.

  4. I-02 Informational Protocol Fee Amount Rounded Down Rounding Acknowledged
    Location
    USD8.sol

    Description

    In the 4th scenario in the claimYield function the protocolFeeAmount is computed using round down division, rounding against the protocol and in favor of the yieldRecipient.

    This however poses almost no impact since the claimYield function is permissioned to be callable only by the admin or the YIELD_RECIPIENT_MANAGER_ROLE and normal usage will only truncate a handful of wei in favor of the yield recipient over the protocol recipient over time.

    Recommendation

    Be aware that the existing protocol fee logic rounds against the protocol and in favor of the yield recipient. If desired, consider using round up logic for the protocol fee amount, however keeping the code unchanged and using division by truncation, while acknowledging this behavior, is more straightforward.

  5. I-03 Informational Unnecessary _beforeClaimYield Override Superfluous Code Resolved
    Location
    USD8.sol

    Description

    In the USD8 contract the _beforeClaimYield function is unnecessarily overridden to add the onlyRole(YIELD_RECIPIENT_MANAGER_ROLE) modifier, however the entire claimYield function has been overridden in the USD8 contract, which entirely bypasses this _beforeClaimYield hook.

    Recommendation

    Either invoke the _beforeClaimYield hook instead of the authorization that is performed at the beginning of the claimYield function, or remove the _beforeClaimYield override entirely.

  6. I-04 Informational Misleading Documentation Documentation Resolved
    Location
    USD8.sol

    Description

    In the NatSpec for the _beforeClaimYield function it is mentioned that Addresses with the YIELD_RECIPIENT_MANAGER_ROLE are still able to claim yield when the contract is paused.

    However the entire claimYield function has been overridden and uses a whenNotPaused modifier, thus contradicting this comment.

    Recommendation

    Clarify what the desired behavior is and if it is desired that certain roles can claim yield even when the system is paused consider removing the whenNotPaused modifier.

  7. I-05 Informational Multiple Initialization Steps Are Necessary Warning Acknowledged
    Location
    USD8.sol

    Description

    Typically the initialize function handles the entire initialization of a contract that is necessary. However the initialize function for the USD8 contract does not instantiate an initial protocol treasury address and therefore a treasury must always be set with the setProtocolTreasuryAddress function before yield can be claimed.

    Recommendation

    Confirm if this secondary initialization step is desired, if not consider including an initial treasury address in the initialize function.

  8. I-06 Informational claimYield Bypasses Freeze List Warning Acknowledged
    Location
    USD8.sol

    Description

    claimYield mints tokens to the yieldRecipient and protocolTreasuryAddress without checking if these accounts are frozen.

    This bypasses the freeze policy applied to wrap/unwrap/transfers and can accumulate balances on frozen accounts, undermining the intent that operations are blocked for frozen addresses.

    Recommendation

    This is likely acceptable given the trusted nature of these addresses and the claimYield function. However be aware of this circumvention of the freeze list.

  9. I-07 Informational Multiple Initialize Functions Risk Warning Acknowledged
    Location
    USD8.sol

    Description

    In the base MYieldToOne contract there is an initialize function which has different parameters and therefore a different function signature than the initialize function declared in the USD8 contract.

    As a result, there are multiple initialize functions with the initializer modifier available to be called on the USD8 contract. This poses a risk if the wrong initialize function is called, incorrectly initializing the proxy and disabling all other initializers.

    Recommendation

    When deploying the USD8 contract ensure that the initialize function that is implemented on the USD8 contract is the one being invoked based upon the parameters being provided.

  10. I-08 Informational Outdated MUSD References Documentation Resolved
    Location
    USD8.sol

    Description

    Throughout the USD8 contract in the documentation for the initialize, _beforeWrap, _beforeUnwrap functions mUSD is referenced instead of USD8.

    Recommendation

    Consider updating the comments to refer to USD8 instead of mUSD.

More from M0

All 10 reports
  1. Liquidity Delivery Updates

    4 findings 4 findings: 1 low, 3 informational
  2. PYUSDX

    21 findings 21 findings: 8 low, 13 informational
  3. Liquidity Delivery

    59 findings3 critical · 5 high 59 findings: 3 critical, 5 high, 10 medium, 14 low, 27 informational
  4. M Extensions Updates

    16 findings 16 findings: 1 medium, 5 low, 10 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote