Coinbase

Coinbase: commerce-payments PR 90

Cantina Security Report

Organization

@coinbase

Engagement Type

Cantina Reviews

Period

-

Researchers


Findings

Informational

3 findings

3 fixed

0 acknowledged


Informational3 findings

  1. Zero-fee documentation conflicts with fixed fee receiver validation

    Severity

    Severity: Informational

    Submitted by

    Sujith S


    Description

    The documentation in docs/Fees.md:133 states that feeAmount = 0 with feeReceiver = address(0) succeeds when PaymentInfo.feeReceiver is a fixed nonzero address. However, _validateFee() unconditionally compares both receiver addresses and reverts with InvalidFeeReceiver. Integrations following the documentation may therefore unexpectedly fail charge() or capture() calls.

    Recommendation

    Align the documentation and implementation. Either require the configured receiver in the example, or skip receiver matching when feeAmount == 0.

    Coinbase: Fixed in 94367139.

    Cantina: Verified fix. No code changes made, the fix is documentation-only.

  2. Capture fragmentation bypasses the aggregate minimum fee

    Severity

    Severity: Informational

    Submitted by

    Sujith S


    Description

    Each capture independently computes minFee = floor(captureAmount × minFeeBps / 10,000). An authorized operator can fragment a payment into sufficiently small captures so every minimum rounds to zero.

    For a zero-decimal token, a 100-token authorization with a fixed 1% fee requires 1 token when captured once, but 100 one-token captures each accept feeAmount = 0. Exploitation is constrained by operator trust (as fee receiver will be the operator at present), transaction costs, and token denomination. If the operator trust model changes this may become profitable for low-decimal high-value tokens.

    Recommendation

    Document this as "accepted risk". Alternatively, explicitly define minFeeBps as a per-capture guarantee and restrict uneconomically small captures.

    Coinbase: Fixed in 94367139.

    Cantina: Verified fix. No code changes made, the fix is documentation-only.

  3. Changed event signatures silently break offchain consumers filtering by topic0

    Severity

    Severity: Informational

    Submitted by

    Cryptara


    Description

    The PR changes uint16 feeBps to uint256 feeAmount in the PaymentCharged and PaymentCaptured events. Because an event's topic0 is the keccak256 of its full signature, both events get new selectors.

    Offchain indexers, reconciliation and monitoring that filter logs by the old topic0 will not error — they will simply observe zero events from the new deployment. The failure mode is silent data loss rather than a crash, so it can go unnoticed.

    Two aggravating factors:

    1. The currently deployed AuthCaptureEscrow instances on Base mainnet/Sepolia emit the old-format events, so both formats will coexist during migration.
    2. The third data field changes meaning from a rate (bps) to an absolute token amount; a consumer migrated to the new address but decoding with the old ABI would misinterpret values (e.g. reading 25e6 as bps) rather than fail.

    Recommendation

    Call out the selector change explicitly in release/migration notes, publish both topic0 values for integrators, and version the deployment docs + SDK ABIs together with the redeployment.

    Coinbase: Fixed in 94367139.

    Cantina: Verified fix. No code changes made, the fix is documentation-only.