Skip to content

[Bug]: dapp transaction with the ERC-20 transfer selector to a non-token contract fails with "Cannot read property 'decimals' of undefined" #7770

Description

@oleksandrrybalko

Summary

eth_sendTransaction from a dapp fails with TypeError: Cannot read property 'decimals' of undefined, before the confirmation sheet is shown, whenever the calldata starts with 0xa9059cbb (the transfer(address,uint256) selector) and the to address is not a token in the user's asset list.

The selector is not reserved for ERC-20s. Any contract that exposes a function named transfer(address,uint256) (routers, escrows, vesting contracts, wrappers, etc.) triggers this, and so does a real ERC-20 the user does not currently hold.

Where

src/features/dapp-request/utils/requests.js, getTransactionDisplayDetails (same in develop and in the v2.0.42 tag, line 107):

if (transaction.data.startsWith(tokenTransferHash)) {          // "0xa9059cbb"
  const contractAddress = transaction.to;
  const accountAssetUniqueId = getUniqueId(contractAddress, chainId);
  const asset = ethereumUtils.getAccountAsset(accountAssetUniqueId); // undefined when `to` is not a held token
  ...
  const value = convertRawAmountToDecimalFormat(convertHexToString(amount), asset.decimals); // throws

Because this runs inside getRequestDisplayDetails while the request is being prepared, the exception propagates back to the dapp as the RPC error. Nothing is signed and no sheet is displayed. Libraries such as viem then wrap the message as if it were a contract revert, which sends dapp developers looking at their contracts first.

Steps to reproduce

  1. Deploy (or use) any contract with a non-ERC-20 transfer(address,uint256) function, e.g.

    contract Relay {
        event Sent(address indexed to, uint256 amount);
        function transfer(address to, uint256 amount) external { emit Sent(to, amount); }
    }
  2. From a dapp connected via WalletConnect or the in-app browser, call relay.transfer(someAddress, 1).

  3. Observe: the request errors immediately with Cannot read property 'decimals' of undefined; no confirmation sheet appears.

Control: the same dapp calling approve(address,uint256) (0x095ea7b3) on the same flow goes through the generic contract-interaction path and works.

Expected

When the to address does not resolve to a known asset, fall through to the generic contract-interaction display (the if (transaction.data) branch below it) instead of throwing. Alternatively guard the lookup (asset?.decimals ?? 18), though falling through is more correct, since the calldata may not be a token transfer at all.

Environment

  • Rainbow iOS/Android, v2.0.42 (code path unchanged on develop as of today)
  • Triggered via dapp request (WalletConnect / in-app browser)

Impact

Any dapp whose contract happens to use this function name is unusable from Rainbow. When the call follows an ERC-20 approval (as routers typically do), the user has already paid gas for an approval that leads nowhere. The error text points at the contract, so it is hard to diagnose from the dapp side.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions