diff --git a/src/components/Balance.test.tsx b/src/components/Balance.test.tsx
new file mode 100644
index 0000000000..79917c16cc
--- /dev/null
+++ b/src/components/Balance.test.tsx
@@ -0,0 +1,62 @@
+/**
+ * @jest-environment jsdom
+ */
+import React from 'react';
+import { publicClient } from '../network/client';
+import { render, screen } from '@testing-library/react';
+import { Balance } from './Balance';
+import { useOnchainActionWithCache } from '../hooks/useOnchainActionWithCache';
+
+import '@testing-library/jest-dom';
+
+jest.mock('../network/client');
+jest.mock('../hooks/useOnchainActionWithCache');
+
+describe('Balance', () => {
+ const testAddress = '0x1234567890abcdef1234567890abcdef12345678';
+
+ const mockGetBalance = publicClient.getBalance as jest.Mock;
+ const mockUseOnchainActionWithCache = useOnchainActionWithCache as jest.Mock;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('displays ETH balance', async () => {
+ const testBalance = 49094208464446850n;
+ mockGetBalance.mockReturnValue(testBalance);
+ mockUseOnchainActionWithCache.mockImplementation(() => {
+ return {
+ data: testBalance,
+ isLoading: false,
+ };
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(await screen.findByTestId('test-id-balance')).toHaveTextContent('0.049 ETH');
+ });
+
+ it('displays ETH balance with rounding', async () => {
+ const testBalance = 49994208464446850n;
+ mockGetBalance.mockReturnValue(testBalance);
+ mockUseOnchainActionWithCache.mockImplementation(() => {
+ return {
+ data: testBalance,
+ isLoading: false,
+ };
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(await screen.findByTestId('test-id-balance')).toHaveTextContent('0.050 ETH');
+ });
+});
diff --git a/src/components/Balance.tsx b/src/components/Balance.tsx
new file mode 100644
index 0000000000..71f5ba0b1a
--- /dev/null
+++ b/src/components/Balance.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import { formatEther, parseEther } from 'viem';
+import type { Address } from 'viem';
+import { publicClient } from '../network/client';
+import { useOnchainActionWithCache } from '../hooks/useOnchainActionWithCache';
+
+type BalanceProps = {
+ address: Address;
+ className?: string;
+ decimalDigits?: number;
+ props?: React.HTMLAttributes;
+};
+
+const balanceAction = (address: Address) => async (): Promise => {
+ try {
+ return await publicClient
+ .getBalance({
+ address,
+ })
+ .toString();
+ } catch (err) {
+ return '0';
+ }
+};
+
+/**
+ * Balance is a React component that renders the account balance for the given Ethereum address.
+ * It displays the user's balance, with the no. of decimals specified by the 'decimalDigits' prop.
+ * By default, 'decimalDigits' is set to 3.
+ *
+ * @param {Address} props.address - The Ethereum address for which to display the balance.
+ * @param {string} [className] - Optional CSS class for custom styling.
+ * @param {number} [decimalDigits=3] - Determines the no. of decimal digits to be displayed.
+ * @param {React.HTMLAttributes} [props] - Additional HTML attributes for the span element.
+ */
+export function Balance({ address, className, decimalDigits = 3, ...props }: BalanceProps) {
+ const balanceKey = `balance-${address}`;
+ const { data: balance, isLoading } = useOnchainActionWithCache(
+ balanceAction(address),
+ balanceKey,
+ );
+
+ if (isLoading) {
+ return null;
+ }
+
+ return (
+
+ {parseFloat(formatEther(BigInt(balance ?? '0'))).toFixed(decimalDigits)}
+ {' ETH'}
+
+ );
+}