Skip to content

Commit

Permalink
add new uti functions
Browse files Browse the repository at this point in the history
  • Loading branch information
alissacrane-cb committed Dec 13, 2024
1 parent 15f0da5 commit 3002cc4
Show file tree
Hide file tree
Showing 8 changed files with 513 additions and 17 deletions.
68 changes: 68 additions & 0 deletions src/core/api/getSwapLiteQuote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { SwapError, SwapUnit } from '../../swap/types';
import { isSwapError } from '../../swap/utils/isSwapError';
import type { Token } from '../../token';
import { formatTokenAmount } from '../utils/formatTokenAmount';
import { getSwapQuote } from './getSwapQuote';
import type {
APIError,
GetSwapQuoteParams,
GetSwapQuoteResponse,
} from './types';

type GetSwapLiteQuoteResponse = {
response?: GetSwapQuoteResponse;
error?: APIError;
formattedFromAmount?: string;
};

type GetSwapLiteQuoteParams = Omit<GetSwapQuoteParams, 'from'> & {
fromSwapUnit: SwapUnit;
from?: Token;
};

export async function getSwapLiteQuote({
amount,
amountReference,
from,
maxSlippage,
to,
useAggregator,
fromSwapUnit,
}: GetSwapLiteQuoteParams): Promise<GetSwapLiteQuoteResponse> {
// only fetch quote if the from token is provided
if (!from) {
return { response: undefined, formattedFromAmount: '', error: undefined };
}

let response: GetSwapQuoteResponse | undefined;
// only fetch quote if the from and to tokens are different
if (to?.symbol !== from?.symbol) {
response = await getSwapQuote({
amount,
amountReference,
from,
maxSlippage,
to,
useAggregator,
});
}

let formattedFromAmount = '';
if (response && !isSwapError(response)) {
formattedFromAmount = formatTokenAmount(
response.fromAmount,
response.from.decimals,
);

fromSwapUnit.setAmountUSD(response?.fromAmountUSD || '');
fromSwapUnit.setAmount(formattedFromAmount || '');
}

let error: SwapError | undefined;
if (isSwapError(response)) {
error = response;
response = undefined;
}

return { response, formattedFromAmount, error };
}
12 changes: 6 additions & 6 deletions src/swap/components/SwapProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ const renderWithProviders = ({
const TestSwapComponent = () => {
const context = useSwapContext();
useEffect(() => {
context.from.setToken(ETH_TOKEN);
context.from.setToken?.(ETH_TOKEN);
context.from.setAmount('100');
context.to.setToken(DEGEN_TOKEN);
context.to.setToken?.(DEGEN_TOKEN);
}, [context]);
const handleStatusError = async () => {
context.updateLifecycleStatus({
Expand Down Expand Up @@ -555,8 +555,8 @@ describe('SwapProvider', () => {
React.useEffect(() => {
const initializeSwap = async () => {
await act(async () => {
from.setToken(ETH_TOKEN);
to.setToken(DEGEN_TOKEN);
from.setToken?.(ETH_TOKEN);
to.setToken?.(DEGEN_TOKEN);
handleToggle();
});
};
Expand Down Expand Up @@ -652,9 +652,9 @@ describe('SwapProvider', () => {
it('should toggle tokens and amounts', async () => {
const { result } = renderHook(() => useSwapContext(), { wrapper });
await act(async () => {
result.current.from.setToken(ETH_TOKEN);
result.current.from.setToken?.(ETH_TOKEN);
result.current.from.setAmount('10');
result.current.to.setToken(DEGEN_TOKEN);
result.current.to.setToken?.(DEGEN_TOKEN);
result.current.to.setAmount('1000');
});
await act(async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/swap/components/SwapProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ export function SwapProvider({
const handleToggle = useCallback(() => {
from.setAmount(to.amount);
to.setAmount(from.amount);
from.setToken(to.token);
to.setToken(from.token);
from.setToken?.(to.token);
to.setToken?.(from.token);

updateLifecycleStatus({
statusName: 'amountChange',
Expand Down
166 changes: 166 additions & 0 deletions src/swap/hooks/useResetSwapLiteInputs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { SwapUnit } from '../types';
import { useResetSwapLiteInputs } from './useResetSwapLiteInputs';

describe('useResetSwapLiteInputs', () => {
const mockFromTokenResponse = {
refetch: vi.fn().mockResolvedValue(undefined),
};
const mockFromETHTokenResponse = {
refetch: vi.fn().mockResolvedValue(undefined),
};
const mockFromUSDCTokenResponse = {
refetch: vi.fn().mockResolvedValue(undefined),
};
const mockToTokenResponse = { refetch: vi.fn().mockResolvedValue(undefined) };
const mockFrom: SwapUnit = {
balance: '100',
balanceResponse: mockFromTokenResponse,
amount: '50',
setAmount: vi.fn(),
setAmountUSD: vi.fn(),
token: undefined,
loading: false,
setLoading: vi.fn(),
error: undefined,
};
const mockFromETH: SwapUnit = {
balance: '100',
balanceResponse: mockFromETHTokenResponse,
amount: '50',
setAmount: vi.fn(),
setAmountUSD: vi.fn(),
token: undefined,
loading: false,
setLoading: vi.fn(),
error: undefined,
};
const mockFromUSDC: SwapUnit = {
balance: '100',
balanceResponse: mockFromUSDCTokenResponse,
amount: '50',
setAmount: vi.fn(),
setAmountUSD: vi.fn(),
token: undefined,
loading: false,
setLoading: vi.fn(),
error: undefined,
};
const mockTo: SwapUnit = {
balance: '200',
balanceResponse: mockToTokenResponse,
amount: '75',
setAmount: vi.fn(),
setAmountUSD: vi.fn(),
token: undefined,
loading: false,
setLoading: vi.fn(),
error: undefined,
};

beforeEach(() => {
vi.clearAllMocks();
});

it('should return a function', () => {
const { result } = renderHook(() =>
useResetSwapLiteInputs({
fromETH: mockFromETH,
fromUSDC: mockFromUSDC,
from: mockFrom,
to: mockTo,
}),
);
expect(typeof result.current).toBe('function');
});

it('should call refetch on responses and set amounts to empty strings when executed', async () => {
const { result } = renderHook(() =>
useResetSwapLiteInputs({
fromETH: mockFromETH,
fromUSDC: mockFromUSDC,
from: mockFrom,
to: mockTo,
}),
);
await act(async () => {
await result.current();
});
expect(mockFromETHTokenResponse.refetch).toHaveBeenCalledTimes(1);
expect(mockToTokenResponse.refetch).toHaveBeenCalledTimes(1);
expect(mockFromETH.setAmount).toHaveBeenCalledWith('');
expect(mockFromETH.setAmountUSD).toHaveBeenCalledWith('');
expect(mockTo.setAmount).toHaveBeenCalledWith('');
expect(mockTo.setAmountUSD).toHaveBeenCalledWith('');
});

it("should not create a new function reference if from and to haven't changed", () => {
const { result, rerender } = renderHook(() =>
useResetSwapLiteInputs({
fromETH: mockFromETH,
fromUSDC: mockFromUSDC,
to: mockTo,
}),
);
const firstRender = result.current;
rerender();
expect(result.current).toBe(firstRender);
});

it('should create a new function reference if from or to change', () => {
const { result, rerender } = renderHook(
({ fromETH, fromUSDC, to }) =>
useResetSwapLiteInputs({
fromETH,
fromUSDC,
to,
}),
{
initialProps: {
fromETH: mockFromETH,
fromUSDC: mockFromUSDC,
to: mockTo,
},
},
);
const firstRender = result.current;
const newMockFromETH = {
...mockFromETH,
balanceResponse: { refetch: vi.fn().mockResolvedValue(undefined) },
};
const newMockFromUSDC = {
...mockFromUSDC,
balanceResponse: { refetch: vi.fn().mockResolvedValue(undefined) },
};
rerender({
fromETH: newMockFromETH,
fromUSDC: newMockFromUSDC,
to: mockTo,
});
expect(result.current).not.toBe(firstRender);
});

it('should handle null responses gracefully', async () => {
const mockFromWithNullResponse = { ...mockFromETH, balanceResponse: null };
const mockFromUSDCWithNullResponse = {
...mockFromUSDC,
balanceResponse: null,
};
const mockToWithNullResponse = { ...mockTo, balanceResponse: null };
const { result } = renderHook(() =>
useResetSwapLiteInputs({
fromETH: mockFromWithNullResponse,
fromUSDC: mockFromUSDCWithNullResponse,
to: mockToWithNullResponse,
}),
);
await act(async () => {
await result.current();
});
expect(mockFromWithNullResponse.setAmount).toHaveBeenCalledWith('');
expect(mockFromWithNullResponse.setAmountUSD).toHaveBeenCalledWith('');
expect(mockToWithNullResponse.setAmount).toHaveBeenCalledWith('');
expect(mockToWithNullResponse.setAmountUSD).toHaveBeenCalledWith('');
});
});
27 changes: 27 additions & 0 deletions src/swap/hooks/useResetSwapLiteInputs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useCallback } from 'react';
import type { SwapLiteTokens } from '../types';

// Refreshes balances and inputs post-swap
export const useResetSwapLiteInputs = ({
fromETH,
fromUSDC,
from,
to,
}: SwapLiteTokens) => {
return useCallback(async () => {
await Promise.all([
from?.balanceResponse?.refetch(),
from?.setAmount(''),
from?.setAmountUSD(''),
fromETH.balanceResponse?.refetch(),
fromETH.setAmount(''),
fromETH.setAmountUSD(''),
fromUSDC.balanceResponse?.refetch(),
fromUSDC.setAmount(''),
fromUSDC.setAmountUSD(''),
to.balanceResponse?.refetch(),
to.setAmount(''),
to.setAmountUSD(''),
]);
}, [from, fromETH, fromUSDC, to]);
};
Loading

0 comments on commit 3002cc4

Please sign in to comment.