diff --git a/src/fund/components/FundCard.tsx b/src/fund/components/FundCard.tsx
index db70c4ebf6..7860cea5a1 100644
--- a/src/fund/components/FundCard.tsx
+++ b/src/fund/components/FundCard.tsx
@@ -3,7 +3,6 @@ import { Children, useMemo } from 'react';
import { useTheme } from '../../core-react/internal/hooks/useTheme';
import { background, border, cn, color, text } from '../../styles/theme';
import { DEFAULT_PAYMENT_METHODS } from '../constants';
-import { useExchangeRate } from '../hooks/useExchangeRate';
import { useFundCardFundingUrl } from '../hooks/useFundCardFundingUrl';
import { useFundCardSetupOnrampEventListeners } from '../hooks/useFundCardSetupOnrampEventListeners';
import type { FundCardContentPropsReact, FundCardPropsReact } from '../types';
@@ -88,11 +87,6 @@ function FundCardContent({
paymentMethods = DEFAULT_PAYMENT_METHODS,
submitButtonComponent,
}: FundCardContentPropsReact) {
- /**
- * Fetches and sets the exchange rate for the asset
- */
- useExchangeRate(assetSymbol);
-
const {
setFundAmountFiat,
fundAmountFiat,
diff --git a/src/fund/components/FundCardAmountInput.tsx b/src/fund/components/FundCardAmountInput.tsx
index 1f70b652d9..3156635228 100644
--- a/src/fund/components/FundCardAmountInput.tsx
+++ b/src/fund/components/FundCardAmountInput.tsx
@@ -107,7 +107,7 @@ export const FundCardAmountInput = ({
`}
-
+
{/* Display the fiat currency sign before the input*/}
{inputType === 'fiat' && currencySign && (
{!hideImage && (
-
+
)}
{paymentMethod.name}
diff --git a/src/fund/components/FundCardPaymentMethodSelectorToggle.tsx b/src/fund/components/FundCardPaymentMethodSelectorToggle.tsx
index fec8d70d8b..64ff9c8ab5 100644
--- a/src/fund/components/FundCardPaymentMethodSelectorToggle.tsx
+++ b/src/fund/components/FundCardPaymentMethodSelectorToggle.tsx
@@ -31,7 +31,7 @@ export const FundCardPaymentMethodSelectorToggle = forwardRef(
data-testid="ockFundCardPaymentMethodSelectorToggle"
>
-
+
('default');
+ const fetchExchangeRate = useDebounce(async () => {
+ setExchangeRateLoading(true);
+ const quote = await fetchOnrampQuote({
+ purchaseCurrency: selectedAsset,
+ paymentCurrency: 'USD',
+ paymentAmount: '100',
+ paymentMethod: 'CARD',
+ country: 'US',
+ });
+
+ setExchangeRateLoading(false);
+
+ setExchangeRate(
+ Number(quote.purchaseAmount.value) / Number(quote.paymentSubtotal.value),
+ );
+ }, 1000);
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies: One time effect
+ useEffect(() => {
+ fetchExchangeRate();
+ }, []);
+
const value = useValue({
selectedAsset,
setSelectedAsset,
diff --git a/src/fund/components/FundProvider.test.tsx b/src/fund/components/FundProvider.test.tsx
index 70f8071d6e..4c75dba348 100644
--- a/src/fund/components/FundProvider.test.tsx
+++ b/src/fund/components/FundProvider.test.tsx
@@ -1,18 +1,47 @@
-import { render, screen } from '@testing-library/react';
+import { setOnchainKitConfig } from '@/core/OnchainKitConfig';
+import { render, screen, waitFor } from '@testing-library/react';
import { act } from 'react';
-import { describe, expect, it } from 'vitest';
+import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest';
import { FundCardProvider, useFundContext } from './FundCardProvider';
+const mockResponseData = {
+ payment_total: { value: '100.00', currency: 'USD' },
+ payment_subtotal: { value: '120.00', currency: 'USD' },
+ purchase_amount: { value: '0.1', currency: 'BTC' },
+ coinbase_fee: { value: '2.00', currency: 'USD' },
+ network_fee: { value: '1.00', currency: 'USD' },
+ quote_id: 'quote-id-123',
+};
+
+global.fetch = vi.fn(() =>
+ Promise.resolve({
+ json: () => Promise.resolve(mockResponseData),
+ }),
+) as Mock;
+
+vi.mock('../../core-react/internal/hooks/useDebounce', () => ({
+ useDebounce: vi.fn((callback) => callback),
+}));
+
const TestComponent = () => {
const context = useFundContext();
return (
{context.selectedAsset}
+ {context.exchangeRate}
+
+ {context.exchangeRateLoading ? 'loading' : 'not-loading'}
+
);
};
describe('FundCardProvider', () => {
+ beforeEach(() => {
+ setOnchainKitConfig({ apiKey: '123456789' });
+ vi.clearAllMocks();
+ });
+
it('provides default context values', () => {
render(
@@ -48,6 +77,34 @@ describe('FundCardProvider', () => {
expect(screen.getByTestId('selected-asset').textContent).toBe('ETH');
});
+ it('fetches and sets exchange rate on mount', async () => {
+ act(() => {
+ render(
+
+
+ ,
+ );
+ });
+
+ // Check initial loading state
+ expect(screen.getByTestId('loading-state').textContent).toBe('loading');
+
+ // Wait for exchange rate to be set
+ await waitFor(() => {
+ expect(screen.getByTestId('exchange-rate').textContent).toBe('0.0008333333333333334');
+ expect(screen.getByTestId('loading-state').textContent).toBe('not-loading');
+ });
+
+ // Verify fetch was called with correct parameters
+ expect(fetch).toHaveBeenCalledWith(
+ expect.stringContaining('/quote'),
+ expect.objectContaining({
+ method: 'POST',
+ body: expect.stringContaining('"purchase_currency":"BTC"'),
+ })
+ );
+ });
+
it('throws error when useFundContext is used outside of FundCardProvider', () => {
const TestOutsideProvider = () => {
useFundContext();
diff --git a/src/fund/hooks/useExchangeRate.test.ts b/src/fund/hooks/useExchangeRate.test.ts
deleted file mode 100644
index 2bdc1a6361..0000000000
--- a/src/fund/hooks/useExchangeRate.test.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { setOnchainKitConfig } from '@/core/OnchainKitConfig';
-import { renderHook } from '@testing-library/react';
-import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest';
-import { useFundContext } from '../components/FundCardProvider';
-import { useExchangeRate } from './useExchangeRate';
-
-const mockResponseData = {
- payment_total: { value: '100.00', currency: 'USD' },
- payment_subtotal: { value: '120.00', currency: 'USD' },
- purchase_amount: { value: '0.1', currency: 'BTC' },
- coinbase_fee: { value: '2.00', currency: 'USD' },
- network_fee: { value: '1.00', currency: 'USD' },
- quote_id: 'quote-id-123',
-};
-
-global.fetch = vi.fn(() =>
- Promise.resolve({
- json: () => Promise.resolve(mockResponseData),
- }),
-) as Mock;
-
-vi.mock('../../core-react/internal/hooks/useDebounce', () => ({
- useDebounce: vi.fn((callback) => callback),
-}));
-
-vi.mock('../components/FundCardProvider', () => ({
- useFundContext: vi.fn(),
-}));
-
-let mockSetExchangeRate = vi.fn();
-let mockSetExchangeRateLoading = vi.fn();
-
-describe('useExchangeRate', () => {
- beforeEach(() => {
- setOnchainKitConfig({ apiKey: '123456789' });
- mockSetExchangeRate = vi.fn();
- mockSetExchangeRateLoading = vi.fn();
- (useFundContext as Mock).mockReturnValue({
- exchangeRateLoading: false,
- setExchangeRate: mockSetExchangeRate,
- setExchangeRateLoading: mockSetExchangeRateLoading,
- });
- });
-
- it('should fetch and set exchange rate correctly', async () => {
- // Mock dependencies
-
- renderHook(() => useExchangeRate('BTC'));
-
- // Assert loading state
- expect(mockSetExchangeRateLoading).toHaveBeenCalledWith(true);
-
- // Wait for the exchange rate to be fetched
- await new Promise((resolve) => setTimeout(resolve, 0));
-
- // Assert loading state is false and exchange rate is set correctly
- expect(mockSetExchangeRateLoading).toHaveBeenCalledWith(false);
- expect(mockSetExchangeRate).toHaveBeenCalledWith(0.0008333333333333334);
- });
-
- it('should not fetch exchange rate if already loading', () => {
- // Mock exchangeRateLoading as true
- (useFundContext as Mock).mockReturnValue({
- exchangeRateLoading: true,
- setExchangeRate: mockSetExchangeRate,
- setExchangeRateLoading: mockSetExchangeRateLoading,
- });
-
- // Render the hook
- renderHook(() => useExchangeRate('BTC'));
-
- // Assert that setExchangeRateLoading was not called
- expect(mockSetExchangeRateLoading).not.toHaveBeenCalled();
-
- // Assert that setExchangeRate was not called
- expect(mockSetExchangeRate).not.toHaveBeenCalled();
- });
-});
diff --git a/src/fund/hooks/useExchangeRate.ts b/src/fund/hooks/useExchangeRate.ts
deleted file mode 100644
index 54c84c784b..0000000000
--- a/src/fund/hooks/useExchangeRate.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { useEffect, useMemo } from 'react';
-import { useDebounce } from '../../core-react/internal/hooks/useDebounce';
-import { useFundContext } from '../components/FundCardProvider';
-import { fetchOnrampQuote } from '../utils/fetchOnrampQuote';
-
-export const useExchangeRate = (assetSymbol: string) => {
- const { setExchangeRate, exchangeRateLoading, setExchangeRateLoading } =
- useFundContext();
-
- const fetchExchangeRate = useDebounce(async () => {
- if (exchangeRateLoading) {
- return;
- }
-
- setExchangeRateLoading(true);
- const quote = await fetchOnrampQuote({
- purchaseCurrency: assetSymbol,
- paymentCurrency: 'USD',
- paymentAmount: '100',
- paymentMethod: 'CARD',
- country: 'US',
- });
-
- setExchangeRateLoading(false);
-
- setExchangeRate(
- Number(quote.purchaseAmount.value) / Number(quote.paymentSubtotal.value),
- );
- }, 1000);
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: One time effect
- useEffect(() => {
- fetchExchangeRate();
- }, []);
-
- return useMemo(() => ({ fetchExchangeRate }), [fetchExchangeRate]);
-};
diff --git a/src/internal/components/TextInput.tsx b/src/internal/components/TextInput.tsx
index dd01b3b2dc..198b2ae30e 100644
--- a/src/internal/components/TextInput.tsx
+++ b/src/internal/components/TextInput.tsx
@@ -13,7 +13,6 @@ type TextInputReact = {
setValue: (s: string) => void;
value: string;
inputValidator?: (s: string) => boolean;
- style?: React.CSSProperties;
};
export function TextInput({
@@ -27,7 +26,6 @@ export function TextInput({
setValue,
value,
inputValidator = () => true,
- style,
}: TextInputReact) {
const handleDebounce = useDebounce((value) => {
onChange(value);
@@ -51,7 +49,6 @@ export function TextInput({
return (
(