Coinbase

Coinbase: MorphoWETHLoanPolicy Review 2

Cantina Security Report

Organization

@coinbase

Engagement Type

Cantina Reviews

Period

-


Findings

Informational

5 findings

0 fixed

5 acknowledged


Informational5 findings

  1. Duplicate market installation across morpho protection variants

    State

    Acknowledged

    Severity

    Severity: Informational

    Submitted by

    slowfi


    Description

    The function _onSingleExecutorInstall from contract MorphoWethLoanProtectionPolicy relies on the inherited activePolicyByMarket mapping to enforce the single active policy constraint for a market. Because that mapping is stored per policy contract instance, the same (account, marketId) can still be installed in MorphoLoanProtectionPolicy and MorphoWethLoanProtectionPolicy at the same time for a WETH collateral market. As a result, the account can hold two independent one-shot top-up authorizations for the same Morpho position, and the effective maximum authorized top-up for that market exceeds the bound implied by a single active protection policy.

    Recommendation

    Consider to enforce the (account, marketId) uniqueness check in shared storage across both Morpho protection variants, or consider to make the ERC20 policy reject WETH collateral markets so only one protection contract can be active for a given Morpho market.

    Coinbase: Acknowledged. No code fixes. Will discuss with product teams to avoid duplicate WETH policies in offchain implementations.

    Cantina Managed: Acknowledged by Coinbase team.

  2. Deploy Function NatSpec Omits MAX_TRIGGER_LTV_RATIO environment variable

    State

    Acknowledged

    Severity

    Severity: Informational

    Submitted by

    slowfi


    Description

    The function deploy from contract Deploy reads three environment variables at runtime: ADMIN, WETH, and MAX_TRIGGER_LTV_RATIO. The @dev NatSpec comment on the function states that it reads only ADMIN and WETH from the environment, omitting any mention of MAX_TRIGGER_LTV_RATIO. The documentation is therefore inconsistent with the implementation, which may lead operators or integrators to believe the function has fewer external dependencies than it actually does.

    Recommendation

    Consider to update the @dev NatSpec on deploy to list all three environment variables it reads, and to note the valid range for MAX_TRIGGER_LTV_RATIO (greater than zero and strictly less than 1e18) so operators understand the accepted input bounds before running the script.

    Coinbase: Acknowledged. Will not fix as part of the audit.

    Cantina Managed: Acknowledged by Coinbase team.

  3. Redundant lowercase WETH view function duplicates public getter

    State

    Acknowledged

    Severity

    Severity: Informational

    Submitted by

    slowfi


    Description

    The function weth from contract MorphoWethLoanProtectionPolicy is an external view function that returns the value of the public immutable state variable WETH. The Solidity compiler automatically generates an external getter for every public state variable using the variable's declared name. Because WETH is declared as public, the compiler already exposes an auto generated getter named WETH(). The manually declared weth() function is therefore a redundant alias that returns the same value through a separate selector without adding any behavior. The same pattern exists in the parent contract for the morpho() function and the MORPHO immutable.

    Recommendation

    Consider to remove the weth view function and rely on the auto generated WETH() getter, unless a lowercase alias is required for a specific interface compatibility reason, in which case the rationale should be documented in the NatSpec.

  4. Missing share slippage protection on morpho vault deposits

    State

    Acknowledged

    Severity

    Severity: Informational

    Submitted by

    slowfi


    Description

    The function _onSingleExecutorExecute from contract MorphoLendPolicy authorizes and executes deposits only in terms of depositAssets, without committing to or validating the amount of vault shares received. The execution path consumes the recurring allowance based on the asset amount and then builds a bare deposit(assets, account) call against the configured Morpho vault.

    Since the vault deposit operation returns minted shares but the policy does not use that return value or perform any post-execution share-balance verification, the account can deposit the full authorized asset amount while receiving materially fewer shares if the vault share price moves adversely before execution. This leaves the recurring allowance consumed even though the effective deposit terms have deteriorated.

    Recommendation

    Consider to extend the execution payload with a minimum acceptable shares bound and reject executions that would mint fewer shares than authorized. Consider to enforce that bound either through a pre-execution share preview or through a same-transaction post-execution share-balance delta check.

    Coinbase: Acknowledged, will not address with onchain checks. Will discuss with offchain products which protect against using new vaults.

    Cantina Managed: Acknowledged by Coinbase team.

  5. A top up may not bring user's ltv below triggerLtv threshold

    State

    Acknowledged

    Severity

    Severity: Informational

    Submitted by

    Akshay Srivastav


    Description

    The MorphoLoanProtectionPolicy is a one shot policy which means that it can only be executed once for an account.

    In MorphoLoanProtectionPolicy._onPostExecute function the postTopUpLtv is only being compared with liquidation ltv of morpho market which means that any top up which doesn't push the account's ltv above lltv is valid and consumes the one shot top up opportunity of the account.

    Ideally it should be enforced that the top up is performed with optimal top up amount which puts the account's ltv in a safer range. One way to approach this is to compare the postTopUpLtv with LoanProtectionPolicyConfig.triggerLtv.

    ...    if (postTopUpLtv >= marketParams.lltv) revert();+   if (postTopUpLtv >= config.triggerLtv) revert();

    Coinbase: Acknowledged, will not address. We want to allow actions that prevent liquidation even if it doesn't make the loan particularly healthy.

    Cantina Managed: Acknowledged by Coinbase team.