From dec9d5af1684d7e1f56e266de28f47ef8da935c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 24 May 2026 09:42:57 +0000 Subject: [PATCH] feat(balances): show native ETH on Arbitrum Fetch and display the safe's native ETH balance on Arbitrum alongside Ethereum, Fuse, and Base. Users who sent ETH to their Arbitrum safe address can now see and transfer those funds from the Send flow. --- constants/tokens.ts | 4 +++- hooks/useBalances.ts | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/constants/tokens.ts b/constants/tokens.ts index b3a7efc07..ec98f721d 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,7 +1,7 @@ import { Token } from '@cryptoalgebra/fuse-sdk'; import { ImageSourcePropType } from 'react-native'; -import { base, fuse, mainnet } from 'viem/chains'; +import { arbitrum, base, fuse, mainnet } from 'viem/chains'; import { BUSD, FUSD_V2, @@ -42,6 +42,7 @@ export const NATIVE_TOKENS: Record = { [mainnet.id]: 'ETH', [fuse.id]: 'fuse-network-token', [base.id]: 'ETH', + [arbitrum.id]: 'ETH', }; /** CoinGecko API coin ids */ @@ -49,6 +50,7 @@ export const NATIVE_COINGECKO_TOKENS: Record = { [mainnet.id]: 'ethereum', [fuse.id]: 'fuse-network-token', [base.id]: 'ethereum', + [arbitrum.id]: 'ethereum', }; export const TOKEN_IMAGES: Record = { diff --git a/hooks/useBalances.ts b/hooks/useBalances.ts index 6cb13ae81..3a769db7d 100644 --- a/hooks/useBalances.ts +++ b/hooks/useBalances.ts @@ -1,7 +1,7 @@ import { useQuery } from '@tanstack/react-query'; import { formatUnits, parseUnits, zeroAddress } from 'viem'; import { getBalance, readContract } from 'viem/actions'; -import { base, fuse, mainnet } from 'viem/chains'; +import { arbitrum, base, fuse, mainnet } from 'viem/chains'; import { NATIVE_COINGECKO_TOKENS, NATIVE_TOKENS } from '@/constants/tokens'; import { fetchCoinSimplePrice, fetchTokenList, fetchTokenPriceUsd } from '@/lib/api'; @@ -111,9 +111,11 @@ const fetchTokenBalances = async (safeAddress: string) => { ethBalance, fuseBalance, baseBalance, + arbitrumBalance, ethPrice, fusePrice, basePrice, + arbitrumPrice, tokenList, ] = await Promise.allSettled([ // Token balances via the data-source dispatcher (Alchemy primary, @@ -142,9 +144,13 @@ const fetchTokenBalances = async (safeAddress: string) => { getBalance(publicClient(base.id), { address: safeAddress as `0x${string}`, }), + getBalance(publicClient(arbitrum.id), { + address: safeAddress as `0x${string}`, + }), fetchTokenPriceUsd(NATIVE_TOKENS[mainnet.id]), fetchTokenPriceUsd(NATIVE_TOKENS[fuse.id]), fetchTokenPriceUsd(NATIVE_TOKENS[base.id]), + fetchTokenPriceUsd(NATIVE_TOKENS[arbitrum.id]), fetchTokenList({ isActive: true, }), @@ -356,6 +362,27 @@ const fetchTokenBalances = async (safeAddress: string) => { }); } + if (arbitrumBalance.status === PromiseStatus.FULFILLED && Number(arbitrumBalance.value)) { + const arbitrumPriceValue = + arbitrumPrice.status === PromiseStatus.FULFILLED ? Number(arbitrumPrice.value) : 0; + const arbitrumEthTokenFromList = tokenListData.find( + token => token.chainId === ARBITRUM_CHAIN_ID && token.symbol === 'ETH', + ); + arbitrumTokens.push({ + contractTickerSymbol: 'ETH', + contractName: 'Ether', + contractAddress: zeroAddress, + balance: arbitrumBalance.value.toString(), + quoteRate: arbitrumPriceValue, + contractDecimals: 18, + type: TokenType.NATIVE, + verified: true, + chainId: ARBITRUM_CHAIN_ID, + commonId: arbitrumEthTokenFromList?.commonId, + tokenId: arbitrumEthTokenFromList?.tokenId, + }); + } + let allTokens = [ ...ethereumTokens, ...fuseTokens,