diff --git a/.changeset/olive-monkeys-unite.md b/.changeset/olive-monkeys-unite.md
new file mode 100644
index 0000000000..96426d9f09
--- /dev/null
+++ b/.changeset/olive-monkeys-unite.md
@@ -0,0 +1,5 @@
+---
+"@coinbase/onchainkit": patch
+---
+
+- **patch**: Add connect wallet functionality to Swap component for disconnected user. By @abcrane123 #1173
diff --git a/playground/nextjs-app-router/components/demo/Swap.tsx b/playground/nextjs-app-router/components/demo/Swap.tsx
index 37025d2d28..ec89093c26 100644
--- a/playground/nextjs-app-router/components/demo/Swap.tsx
+++ b/playground/nextjs-app-router/components/demo/Swap.tsx
@@ -9,11 +9,10 @@ import {
} from '@coinbase/onchainkit/swap';
import type { Token } from '@coinbase/onchainkit/token';
import { useCallback, useContext } from 'react';
-import { useAccount } from 'wagmi';
+import { base } from 'viem/chains';
import { AppContext } from '../AppProvider';
function SwapComponent() {
- const { address } = useAccount();
const { chainId } = useContext(AppContext);
const degenToken: Token = {
@@ -23,7 +22,7 @@ function SwapComponent() {
decimals: 18,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
- chainId: 8453,
+ chainId: base.id,
};
const ethToken: Token = {
@@ -33,7 +32,7 @@ function SwapComponent() {
decimals: 18,
image:
'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
- chainId: 8453,
+ chainId: base.id,
};
const usdcToken: Token = {
@@ -43,7 +42,7 @@ function SwapComponent() {
decimals: 6,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/44/2b/442b80bd16af0c0d9b22e03a16753823fe826e5bfd457292b55fa0ba8c1ba213-ZWUzYjJmZGUtMDYxNy00NDcyLTg0NjQtMWI4OGEwYjBiODE2',
- chainId: 8453,
+ chainId: base.id,
};
const wethToken: Token = {
@@ -53,7 +52,7 @@ function SwapComponent() {
decimals: 6,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/47/bc/47bc3593c2dec7c846b66b7ba5f6fa6bd69ec34f8ebb931f2a43072e5aaac7a8-YmUwNmRjZDUtMjczYy00NDFiLWJhZDUtMzgwNjFmYWM0Njkx',
- chainId: 8453,
+ chainId: base.id,
};
const swappableTokens = [degenToken, ethToken, usdcToken, wethToken];
@@ -63,53 +62,48 @@ function SwapComponent() {
}, []);
return (
-
- {address ? (
- chainId !== 8453 ? (
-
-
- Swap Demo is only available on Base.
-
- Please change your chain in settings.
-
-
- ) : (
- <>>
- )
- ) : (
+
+ {chainId !== base.id ? (
- Swap Demo requires wallet.
+ Swap Demo is only available on Base.
- Please connect in settings.
+ You're connected to a different network. Switch to Base to continue
+ using the app.
- )}
- {address ? (
-
-
-
-
-
-
-
) : (
<>>
)}
+
+ {ENVIRONMENT_VARIABLES[ENVIRONMENT.ENVIRONMENT] === 'production' &&
+ chainId === base.id ? (
+
+ Note: Swap is disabled on production. To test, run the app locally.
+
+ ) : null}
+
+
+
+
+
+
+
+
);
}
diff --git a/src/swap/components/SwapAmountInput.test.tsx b/src/swap/components/SwapAmountInput.test.tsx
index 7aa04e46a8..affbe040d4 100644
--- a/src/swap/components/SwapAmountInput.test.tsx
+++ b/src/swap/components/SwapAmountInput.test.tsx
@@ -34,6 +34,7 @@ vi.mock('./SwapProvider', () => ({
const useSwapContextMock = useSwapContext as Mock;
const mockContextValue = {
+ address: '0x123',
from: {
amount: '10',
balance: '0.0002851826238227',
@@ -87,6 +88,14 @@ describe('SwapAmountInput', () => {
).not.toBeInTheDocument();
});
+ it('should not render max button if wallet not connected', () => {
+ useSwapContextMock.mockReturnValue({ ...mockContextValue, address: '' });
+ render(
);
+ expect(
+ screen.queryByTestId('ockSwapAmountInput_MaxButton'),
+ ).not.toBeInTheDocument();
+ });
+
it('should update input value with balance amount on max button click', () => {
useSwapContextMock.mockReturnValue(mockContextValue);
render(
);
diff --git a/src/swap/components/SwapAmountInput.tsx b/src/swap/components/SwapAmountInput.tsx
index 63fc10fb6d..99f9609ba3 100644
--- a/src/swap/components/SwapAmountInput.tsx
+++ b/src/swap/components/SwapAmountInput.tsx
@@ -17,7 +17,7 @@ export function SwapAmountInput({
type,
swappableTokens,
}: SwapAmountInputReact) {
- const { to, from, handleAmountChange } = useSwapContext();
+ const { address, to, from, handleAmountChange } = useSwapContext();
const source = useValue(type === 'from' ? from : to);
const destination = useValue(type === 'from' ? to : from);
@@ -82,7 +82,7 @@ export function SwapAmountInput({
className={cn(
'w-full border-[none] bg-transparent font-display text-[2.5rem]',
'leading-none outline-none',
- hasInsufficientBalance ? color.error : color.foreground,
+ hasInsufficientBalance && address ? color.error : color.foreground,
)}
placeholder="0.0"
delayMs={delayMs}
@@ -112,7 +112,7 @@ export function SwapAmountInput({
className={cn(text.label2, color.foregroundMuted)}
>{`Balance: ${getRoundedAmount(source.balance, 8)}`}
)}
- {type === 'from' && (
+ {type === 'from' && address && (
({
+ useAccount: vi.fn(),
+ useConnect: vi.fn(),
+}));
+
vi.mock('./SwapProvider', () => ({
useSwapContext: vi.fn(),
}));
@@ -20,78 +26,91 @@ describe('SwapButton', () => {
mockHandleSubmit.mockClear();
});
- it('renders button with text "Swap" when not loading', () => {
+ it('should render button with text "Swap" when not loading', () => {
useSwapContextMock.mockReturnValue({
+ address: '0x123',
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
handleSubmit: mockHandleSubmit,
});
-
render( );
-
const button = screen.getByTestId('ockSwapButton_Button');
expect(button).toHaveTextContent('Swap');
expect(button).not.toBeDisabled();
});
- it('renders Spinner when loading', () => {
+ it('should render Spinner when loading', () => {
useSwapContextMock.mockReturnValue({
to: { loading: true, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
handleSubmit: mockHandleSubmit,
});
-
render( );
-
const button = screen.getByTestId('ockSwapButton_Button');
expect(screen.getByTestId('spinner')).toBeInTheDocument();
expect(button).toBeDisabled();
});
- it('button is disabled when required fields are missing', () => {
+ it('should disable button when required fields are missing', () => {
useSwapContextMock.mockReturnValue({
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: null, token: 'BTC' },
loading: false,
handleSubmit: mockHandleSubmit,
});
-
render( );
-
const button = screen.getByTestId('ockSwapButton_Button');
expect(button).toBeDisabled();
});
- it('calls handleSubmit with mockHandleSubmit when clicked', () => {
+ it('should call handleSubmit with mockHandleSubmit when clicked', () => {
useSwapContextMock.mockReturnValue({
+ address: '0x123',
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
handleSubmit: mockHandleSubmit,
});
-
render( );
-
const button = screen.getByTestId('ockSwapButton_Button');
fireEvent.click(button);
-
expect(mockHandleSubmit).toHaveBeenCalled();
});
- it('applies additional className correctly', () => {
+ it('should apply additional className correctly', () => {
useSwapContextMock.mockReturnValue({
+ address: '0x123',
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
handleSubmit: mockHandleSubmit,
});
-
const customClass = 'custom-class';
render( );
-
const button = screen.getByTestId('ockSwapButton_Button');
expect(button).toHaveClass(customClass);
});
+
+ it('should render ConnectWallet if disconnected and no missing fields', () => {
+ useSwapContextMock.mockReturnValue({
+ to: { loading: false, amount: 1, token: 'ETH' },
+ from: { loading: false, amount: 1, token: 'BTC' },
+ loading: false,
+ handleSubmit: mockHandleSubmit,
+ });
+ vi.mocked(useAccount).mockReturnValue({
+ address: '',
+ status: 'disconnected',
+ });
+ vi.mocked(useConnect).mockReturnValue({
+ connectors: [{ id: 'mockConnector' }],
+ connect: vi.fn(),
+ status: 'idle',
+ });
+ render( );
+ const button = screen.getByTestId('ockConnectWallet_Container');
+ expect(button).toBeDefined();
+ });
});
diff --git a/src/swap/components/SwapButton.tsx b/src/swap/components/SwapButton.tsx
index 4bac17c892..68dbb7d123 100644
--- a/src/swap/components/SwapButton.tsx
+++ b/src/swap/components/SwapButton.tsx
@@ -1,10 +1,11 @@
import { Spinner } from '../../internal/components/Spinner';
import { background, cn, color, pressable, text } from '../../styles/theme';
+import { ConnectWallet } from '../../wallet';
import type { SwapButtonReact } from '../types';
import { useSwapContext } from './SwapProvider';
export function SwapButton({ className, disabled = false }: SwapButtonReact) {
- const { to, from, loading, isTransactionPending, handleSubmit } =
+ const { address, to, from, loading, isTransactionPending, handleSubmit } =
useSwapContext();
const isLoading =
@@ -18,6 +19,11 @@ export function SwapButton({ className, disabled = false }: SwapButtonReact) {
disabled ||
isLoading;
+ // prompt user to connect wallet
+ if (!isDisabled && !address) {
+ return ;
+ }
+
return (
{
mockGetSwapMessage.mockClear();
});
- test('renders message returned by getSwapMessage', () => {
+ it('should render message returned by getSwapMessage', () => {
const mockMessage = 'Swap message';
const mockContext = {
to: {},
from: {},
error: null,
loading: false,
+ lifeCycleStatus: { statusData: null },
};
-
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
-
render( );
-
const messageDiv = screen.getByTestId('ockSwapMessage_Message');
expect(messageDiv).toHaveTextContent(mockMessage);
expect(messageDiv).toHaveClass('test-class');
});
- test('renders with error message', () => {
+ it('should render with error message', () => {
const mockMessage = 'Error occurred';
const mockContext = {
to: {},
from: {},
error: 'Error occurred',
loading: false,
+ lifeCycleStatus: { statusData: null },
};
-
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
-
render( );
-
const messageDiv = screen.getByTestId('ockSwapMessage_Message');
expect(messageDiv).toHaveTextContent(mockMessage);
});
- test('renders with loading message', () => {
+ it('should render with loading message', () => {
const mockMessage = 'Loading...';
const mockContext = {
to: {},
from: {},
error: null,
loading: true,
+ lifeCycleStatus: { statusData: null },
};
-
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
-
render( );
-
const messageDiv = screen.getByTestId('ockSwapMessage_Message');
expect(messageDiv).toHaveTextContent(mockMessage);
});
- test('applies additional className correctly', () => {
+ it('should apply additional className correctly', () => {
const mockContext = {
to: {},
from: {},
error: null,
loading: false,
+ lifeCycleStatus: { statusData: null },
};
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue('');
-
const customClass = 'custom-class';
render( );
-
const messageDiv = screen.getByTestId('ockSwapMessage_Message');
expect(messageDiv).toHaveClass(customClass);
});
+
+ it('should set isMissingRequiredFields to true when reflected in statusData', () => {
+ const mockContext = {
+ to: { amount: 1, token: 'ETH' },
+ from: { amount: null, token: 'DAI' },
+ error: null,
+ loading: false,
+ isTransactionPending: false,
+ address: '0x123',
+ lifeCycleStatus: { statusData: { isMissingRequiredField: true } },
+ };
+ useSwapContextMock.mockReturnValue(mockContext);
+ render( );
+ expect(mockGetSwapMessage).toHaveBeenCalledWith({
+ address: '0x123',
+ error: null,
+ from: { amount: null, token: 'DAI' },
+ loading: false,
+ isMissingRequiredFields: true,
+ isTransactionPending: false,
+ to: { amount: 1, token: 'ETH' },
+ });
+ });
});
diff --git a/src/swap/components/SwapMessage.tsx b/src/swap/components/SwapMessage.tsx
index 1ccc61f9a3..63257a4a58 100644
--- a/src/swap/components/SwapMessage.tsx
+++ b/src/swap/components/SwapMessage.tsx
@@ -4,12 +4,27 @@ import { getSwapMessage } from '../utils/getSwapMessage';
import { useSwapContext } from './SwapProvider';
export function SwapMessage({ className }: SwapMessageReact) {
- const { to, from, error, loading, isTransactionPending } = useSwapContext();
+ const {
+ address,
+ to,
+ from,
+ error,
+ loading,
+ isTransactionPending,
+ lifeCycleStatus: { statusData },
+ } = useSwapContext();
+
+ const isMissingRequiredFields =
+ !!statusData &&
+ 'isMissingRequiredField' in statusData &&
+ statusData?.isMissingRequiredField;
const message = getSwapMessage({
+ address,
error,
from,
loading,
+ isMissingRequiredFields,
isTransactionPending,
to,
});
diff --git a/src/swap/components/SwapProvider.test.tsx b/src/swap/components/SwapProvider.test.tsx
index 866611d7b1..a1f465f3cd 100644
--- a/src/swap/components/SwapProvider.test.tsx
+++ b/src/swap/components/SwapProvider.test.tsx
@@ -107,7 +107,11 @@ const TestSwapComponent = () => {
const handleStatusAmountChange = async () => {
context.setLifeCycleStatus({
statusName: 'amountChange',
- statusData: null,
+ statusData: {
+ amountFrom: '',
+ amountTo: '',
+ isMissingRequiredField: false,
+ },
});
};
const handleStatusTransactionPending = async () => {
@@ -264,6 +268,84 @@ describe('SwapProvider', () => {
expect(onStatusMock).toHaveBeenCalled();
});
+ it('should update lifecycle status correctly after fetching quote for to token', async () => {
+ vi.mocked(getSwapQuote).mockResolvedValueOnce({
+ toAmount: '10',
+ to: {
+ decimals: 10,
+ },
+ });
+ const { result } = renderHook(() => useSwapContext(), { wrapper });
+ await act(async () => {
+ result.current.handleAmountChange('from', '10', ETH_TOKEN, DEGEN_TOKEN);
+ });
+ expect(result.current.lifeCycleStatus).toStrictEqual({
+ statusName: 'amountChange',
+ statusData: {
+ amountFrom: '10',
+ amountTo: '1e-9',
+ isMissingRequiredField: false,
+ tokenFrom: {
+ address: '',
+ name: 'ETH',
+ symbol: 'ETH',
+ chainId: 8453,
+ decimals: 18,
+ image:
+ 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ },
+ tokenTo: {
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ name: 'DEGEN',
+ symbol: 'DEGEN',
+ chainId: 8453,
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ },
+ },
+ });
+ });
+
+ it('should update lifecycle status correctly after fetching quote for from token', async () => {
+ vi.mocked(getSwapQuote).mockResolvedValueOnce({
+ toAmount: '10',
+ to: {
+ decimals: 10,
+ },
+ });
+ const { result } = renderHook(() => useSwapContext(), { wrapper });
+ await act(async () => {
+ result.current.handleAmountChange('to', '10', ETH_TOKEN, DEGEN_TOKEN);
+ });
+ expect(result.current.lifeCycleStatus).toStrictEqual({
+ statusName: 'amountChange',
+ statusData: {
+ amountFrom: '1e-9',
+ amountTo: '10',
+ isMissingRequiredField: false,
+ tokenTo: {
+ address: '',
+ name: 'ETH',
+ symbol: 'ETH',
+ chainId: 8453,
+ decimals: 18,
+ image:
+ 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ },
+ tokenFrom: {
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ name: 'DEGEN',
+ symbol: 'DEGEN',
+ chainId: 8453,
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ },
+ },
+ });
+ });
+
it('should emit onStatus when setLifeCycleStatus is called with transactionPending', async () => {
const onStatusMock = vi.fn();
renderWithProviders({
diff --git a/src/swap/components/SwapProvider.tsx b/src/swap/components/SwapProvider.tsx
index 52dd96535a..7516bab465 100644
--- a/src/swap/components/SwapProvider.tsx
+++ b/src/swap/components/SwapProvider.tsx
@@ -53,7 +53,9 @@ export function SwapProvider({
const [isTransactionPending, setPendingTransaction] = useState(false);
const [lifeCycleStatus, setLifeCycleStatus] = useState({
statusName: 'init',
- statusData: null,
+ statusData: {
+ isMissingRequiredField: true,
+ },
}); // Component lifecycle
const { from, to } = useFromTo(address);
const { sendTransactionAsync } = useSendTransaction(); // Sending the transaction (and approval, if applicable)
@@ -116,7 +118,19 @@ export function SwapProvider({
source.token = sToken ?? source.token;
destination.token = dToken ?? destination.token;
+ // if token is missing alert user via isMissingRequiredField
if (source.token === undefined || destination.token === undefined) {
+ setLifeCycleStatus({
+ statusName: 'amountChange',
+ statusData: {
+ amountFrom: from.amount,
+ amountTo: to.amount,
+ tokenFrom: from.token,
+ tokenTo: to.token,
+ // token is missing
+ isMissingRequiredField: true,
+ },
+ });
return;
}
if (amount === '' || amount === '.' || Number.parseFloat(amount) === 0) {
@@ -128,7 +142,17 @@ export function SwapProvider({
destination.setLoading(true);
setLifeCycleStatus({
statusName: 'amountChange',
- statusData: null,
+ statusData: {
+ // when fetching quote, the previous
+ // amount is irrelevant
+ amountFrom: type === 'from' ? amount : '',
+ amountTo: type === 'to' ? amount : '',
+ tokenFrom: from.token,
+ tokenTo: to.token,
+ // when fetching quote, the destination
+ // amount is missing
+ isMissingRequiredField: true,
+ },
});
try {
@@ -158,6 +182,18 @@ export function SwapProvider({
response.to.decimals,
);
destination.setAmount(formattedAmount);
+ setLifeCycleStatus({
+ statusName: 'amountChange',
+ statusData: {
+ amountFrom: type === 'from' ? amount : formattedAmount,
+ amountTo: type === 'to' ? amount : formattedAmount,
+ tokenFrom: from.token,
+ tokenTo: to.token,
+ // if quote was fetched successfully, we
+ // have all required fields
+ isMissingRequiredField: !formattedAmount,
+ },
+ });
} catch (err) {
setLifeCycleStatus({
statusName: 'error',
@@ -181,7 +217,9 @@ export function SwapProvider({
}
setLifeCycleStatus({
statusName: 'init',
- statusData: null,
+ statusData: {
+ isMissingRequiredField: false,
+ },
});
try {
@@ -238,6 +276,7 @@ export function SwapProvider({
]);
const value = useValue({
+ address,
error,
from,
loading,
diff --git a/src/swap/types.ts b/src/swap/types.ts
index 80bea7587a..86f4c3e7dc 100644
--- a/src/swap/types.ts
+++ b/src/swap/types.ts
@@ -31,9 +31,11 @@ export type Fee = {
};
export type GetSwapMessageParams = {
+ address?: Address;
error?: SwapError;
loading?: boolean;
isTransactionPending?: boolean;
+ isMissingRequiredFields?: boolean;
to: SwapUnit;
from: SwapUnit;
};
@@ -56,7 +58,9 @@ export type QuoteWarning = {
export type LifeCycleStatus =
| {
statusName: 'init';
- statusData: null;
+ statusData: {
+ isMissingRequiredField: boolean;
+ };
}
| {
statusName: 'error';
@@ -64,7 +68,13 @@ export type LifeCycleStatus =
}
| {
statusName: 'amountChange';
- statusData: null;
+ statusData: {
+ amountFrom: string;
+ amountTo: string;
+ tokenFrom?: Token;
+ tokenTo?: Token;
+ isMissingRequiredField: boolean;
+ };
}
| {
statusName: 'transactionPending';
@@ -121,6 +131,7 @@ export type SwapButtonReact = {
};
export type SwapContextType = {
+ address?: Address; // Used to check if user is connected in SwapButton
error?: SwapError;
from: SwapUnit;
lifeCycleStatus: LifeCycleStatus;
diff --git a/src/swap/utils/getSwapMessage.test.ts b/src/swap/utils/getSwapMessage.test.ts
index 91320fcf4a..0516bbe1d1 100644
--- a/src/swap/utils/getSwapMessage.test.ts
+++ b/src/swap/utils/getSwapMessage.test.ts
@@ -12,6 +12,7 @@ import { SwapMessage, getSwapMessage } from './getSwapMessage';
describe('getSwapMessage', () => {
const baseParams = {
+ address: '0x123',
error: undefined,
from: {
error: undefined,
@@ -33,6 +34,7 @@ describe('getSwapMessage', () => {
setToken: vi.fn(),
},
loading: false,
+ isMissingRequiredFields: false,
};
it('should return BALANCE_ERROR when from or to has an error', () => {
@@ -96,25 +98,9 @@ describe('getSwapMessage', () => {
it('should return INCOMPLETE_FIELD when required fields are missing', () => {
const params = {
...baseParams,
+ isMissingRequiredFields: true,
};
expect(getSwapMessage(params)).toBe(SwapMessage.INCOMPLETE_FIELD);
-
- const params2 = {
- ...baseParams,
- from: {
- ...baseParams.from,
- amount: '10',
- balance: '20',
- token: ETH_TOKEN,
- },
- };
- expect(getSwapMessage(params2)).toBe(SwapMessage.INCOMPLETE_FIELD);
-
- const params3 = {
- ...baseParams,
- to: { ...baseParams.to, amount: '10', token: USDC_TOKEN },
- };
- expect(getSwapMessage(params3)).toBe(SwapMessage.INCOMPLETE_FIELD);
});
it('should return TOO_MANY_REQUESTS when error code is TOO_MANY_REQUESTS_ERROR_CODE', () => {
diff --git a/src/swap/utils/getSwapMessage.ts b/src/swap/utils/getSwapMessage.ts
index 6dba0499a8..55b75e59ba 100644
--- a/src/swap/utils/getSwapMessage.ts
+++ b/src/swap/utils/getSwapMessage.ts
@@ -15,9 +15,11 @@ export enum SwapMessage {
}
export function getSwapMessage({
+ address,
error,
from,
loading,
+ isMissingRequiredFields,
isTransactionPending,
to,
}: GetSwapMessageParams) {
@@ -25,8 +27,8 @@ export function getSwapMessage({
if (from.error || to.error) {
return SwapMessage.BALANCE_ERROR;
}
- // handle amount exceeds balance
- if (Number(from.balance) < Number(from.amount)) {
+ // handle amount exceeds balance (if connected)
+ if (address && Number(from.balance) < Number(from.amount)) {
return SwapMessage.INSUFFICIENT_BALANCE;
}
// handle pending transaction
@@ -41,7 +43,7 @@ export function getSwapMessage({
return SwapMessage.FETCHING_QUOTE;
}
// missing required fields
- if (!from.amount || !from.token || !to.amount || !to.token) {
+ if (isMissingRequiredFields) {
return SwapMessage.INCOMPLETE_FIELD;
}
if (!error) {
diff --git a/src/wallet/components/Wallet.tsx b/src/wallet/components/Wallet.tsx
index 49a3576f2e..92d5987c87 100644
--- a/src/wallet/components/Wallet.tsx
+++ b/src/wallet/components/Wallet.tsx
@@ -1,12 +1,13 @@
import { Children, useEffect, useMemo, useRef } from 'react';
import { findComponent } from '../../internal/utils/findComponent';
+import { cn } from '../../styles/theme';
import { useIsMounted } from '../../useIsMounted';
import type { WalletReact } from '../types';
import { ConnectWallet } from './ConnectWallet';
import { WalletDropdown } from './WalletDropdown';
import { WalletProvider, useWalletContext } from './WalletProvider';
-const WalletContent = ({ children }: WalletReact) => {
+const WalletContent = ({ children, className }: WalletReact) => {
const { isOpen, setIsOpen } = useWalletContext();
const walletContainerRef = useRef(null);
@@ -36,14 +37,17 @@ const WalletContent = ({ children }: WalletReact) => {
}, [isOpen, setIsOpen]);
return (
-
+
{connect}
{isOpen && dropdown}
);
};
-export const Wallet = ({ children }: WalletReact) => {
+export const Wallet = ({ children, className }: WalletReact) => {
const isMounted = useIsMounted();
// prevents SSR hydration issue
@@ -53,7 +57,7 @@ export const Wallet = ({ children }: WalletReact) => {
return (
- {children}
+ {children}
);
};
diff --git a/src/wallet/types.ts b/src/wallet/types.ts
index a87dc65cba..63c792d99c 100644
--- a/src/wallet/types.ts
+++ b/src/wallet/types.ts
@@ -71,6 +71,7 @@ export type WalletContextType = {
*/
export type WalletReact = {
children: React.ReactNode;
+ className?: string;
};
/**