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
-
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); }
}
-
From a dapp connected via WalletConnect or the in-app browser, call relay.transfer(someAddress, 1).
-
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.
Summary
eth_sendTransactionfrom a dapp fails withTypeError: Cannot read property 'decimals' of undefined, before the confirmation sheet is shown, whenever the calldata starts with0xa9059cbb(thetransfer(address,uint256)selector) and thetoaddress 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 indevelopand in thev2.0.42tag, line 107):Because this runs inside
getRequestDisplayDetailswhile 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
Deploy (or use) any contract with a non-ERC-20
transfer(address,uint256)function, e.g.From a dapp connected via WalletConnect or the in-app browser, call
relay.transfer(someAddress, 1).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
toaddress does not resolve to a known asset, fall through to the generic contract-interaction display (theif (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
developas of today)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.