Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Swap Settings SwapProvider #1176

Merged
merged 10 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions playground/nextjs-app-router/components/demo/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
SwapAmountInput,
SwapButton,
SwapMessage,
SwapSettings,
SwapSettingsSlippageDescription,
SwapSettingsSlippageInput,
SwapSettingsSlippageTitle,
SwapToggleButton,
} from '@coinbase/onchainkit/swap';
import type { Token } from '@coinbase/onchainkit/token';
Expand Down Expand Up @@ -84,6 +88,14 @@ function SwapComponent() {
) : null}

<Swap className="border" onStatus={handleOnStatus}>
<SwapSettings>
<SwapSettingsSlippageTitle>Max. slippage</SwapSettingsSlippageTitle>
<SwapSettingsSlippageDescription>
Your swap will revert if the prices change by more than the selected
percentage.
</SwapSettingsSlippageDescription>
<SwapSettingsSlippageInput />
</SwapSettings>
<SwapAmountInput
label="Sell"
swappableTokens={swappableTokens}
Expand Down
30 changes: 18 additions & 12 deletions src/swap/components/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SwapAmountInput } from './SwapAmountInput';
import { SwapButton } from './SwapButton';
import { SwapMessage } from './SwapMessage';
import { SwapProvider } from './SwapProvider';
import { SwapSettings } from './SwapSettings';
import { SwapToggleButton } from './SwapToggleButton';

export function Swap({
Expand All @@ -18,23 +19,24 @@ export function Swap({
onSuccess,
title = 'Swap',
}: SwapReact) {
const { inputs, toggleButton, swapButton, swapMessage } = useMemo(() => {
const childrenArray = Children.toArray(children);
return {
inputs: childrenArray.filter(findComponent(SwapAmountInput)),
toggleButton: childrenArray.find(findComponent(SwapToggleButton)),
swapButton: childrenArray.find(findComponent(SwapButton)),
swapMessage: childrenArray.find(findComponent(SwapMessage)),
};
}, [children]);
const { inputs, toggleButton, swapButton, swapMessage, swapSettings } =
useMemo(() => {
const childrenArray = Children.toArray(children);
return {
inputs: childrenArray.filter(findComponent(SwapAmountInput)),
toggleButton: childrenArray.find(findComponent(SwapToggleButton)),
swapButton: childrenArray.find(findComponent(SwapButton)),
swapMessage: childrenArray.find(findComponent(SwapMessage)),
swapSettings: childrenArray.find(findComponent(SwapSettings)),
};
}, [children]);

const isMounted = useIsMounted();

// prevents SSR hydration issue
if (!isMounted) {
return null;
}

return (
<SwapProvider
experimental={experimental}
Expand All @@ -50,10 +52,14 @@ export function Swap({
)}
data-testid="ockSwap_Container"
>
<div className="mb-4">
<h3 className={text.title3} data-testid="ockSwap_Title">
<div className="mb-4 flex items-center justify-between">
cpcramer marked this conversation as resolved.
Show resolved Hide resolved
cpcramer marked this conversation as resolved.
Show resolved Hide resolved
<h3
className={cn(text.title3, 'text-inherit')}
data-testid="ockSwap_Title"
>
{title}
</h3>
{swapSettings}
</div>
{inputs[0]}
<div className="relative h-1">{toggleButton}</div>
Expand Down
20 changes: 14 additions & 6 deletions src/swap/components/SwapProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ vi.mock('wagmi', async (importOriginal) => {
};
});

vi.mock('../path/to/maxSlippageModule', () => ({
getMaxSlippage: vi.fn().mockReturnValue(10),
}));

const queryClient = new QueryClient();

const config = createConfig({
Expand Down Expand Up @@ -443,12 +447,16 @@ describe('SwapProvider', () => {
});
const button = screen.getByText('setLifeCycleStatus.success');
fireEvent.click(button);
expect(onStatusMock).toHaveBeenCalledWith({
statusName: 'init',
statusData: {
isMissingRequiredField: false,
maxSlippage: 10,
},
await waitFor(() => {
expect(onStatusMock).toHaveBeenCalledWith(
expect.objectContaining({
statusName: 'init',
statusData: expect.objectContaining({
isMissingRequiredField: false,
maxSlippage: 3,
}),
}),
);
});
});

Expand Down
18 changes: 10 additions & 8 deletions src/swap/components/SwapProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { formatTokenAmount } from '../../internal/utils/formatTokenAmount';
import type { Token } from '../../token';
import { GENERIC_ERROR_MESSAGE } from '../../transaction/constants';
import { isUserRejectedRequestError } from '../../transaction/utils/isUserRejectedRequestError';
import { DEFAULT_MAX_SLIPPAGE } from '../constants';
import { useFromTo } from '../hooks/useFromTo';
import { useResetInputs } from '../hooks/useResetInputs';
import type {
Expand Down Expand Up @@ -46,8 +47,8 @@ export function SwapProvider({
const { address } = useAccount();
// Feature flags
const { useAggregator } = experimental;
const [maxSlippage, _setMaxSlippage] = useState(
experimental.maxSlippage || 3,
const [initialMaxSlippage, _setInitialMaxSlippage] = useState(
experimental.maxSlippage || DEFAULT_MAX_SLIPPAGE,
cpcramer marked this conversation as resolved.
Show resolved Hide resolved
);
// Core Hooks
const config = useConfig();
Expand All @@ -58,7 +59,7 @@ export function SwapProvider({
statusName: 'init',
statusData: {
isMissingRequiredField: true,
maxSlippage,
maxSlippage: initialMaxSlippage,
},
}); // Component lifecycle
const [hasHandledSuccess, setHasHandledSuccess] = useState(false);
Expand Down Expand Up @@ -117,6 +118,7 @@ export function SwapProvider({
}, [hasHandledSuccess, lifeCycleStatus.statusName, resetInputs]);

useEffect(() => {
const maxSlippage = lifeCycleStatus.statusData.maxSlippage;
// Reset status to init after success has been handled
if (lifeCycleStatus.statusName === 'success' && hasHandledSuccess) {
setLifeCycleStatus({
Expand All @@ -132,7 +134,6 @@ export function SwapProvider({
hasHandledSuccess,
lifeCycleStatus.statusData,
lifeCycleStatus.statusName,
maxSlippage,
]);

const handleToggle = useCallback(() => {
Expand All @@ -150,6 +151,7 @@ export function SwapProvider({
dToken?: Token,
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: TODO Refactor this component
) => {
const maxSlippage = lifeCycleStatus.statusData.maxSlippage;
const source = type === 'from' ? from : to;
const destination = type === 'from' ? to : from;

Expand Down Expand Up @@ -200,8 +202,8 @@ export function SwapProvider({
amount,
amountReference: 'from',
from: source.token,
maxSlippage: String(maxSlippage),
to: destination.token,
maxSlippage: maxSlippage.toString(),
useAggregator,
});
// If request resolves to error response set the quoteError
Expand Down Expand Up @@ -257,13 +259,14 @@ export function SwapProvider({
destination.setLoading(false);
}
},
[from, lifeCycleStatus, maxSlippage, to, useAggregator],
[from, lifeCycleStatus, to, useAggregator],
);

const handleSubmit = useCallback(async () => {
if (!address || !from.token || !to.token || !from.amount) {
return;
}
const maxSlippage = lifeCycleStatus.statusData.maxSlippage;
setLifeCycleStatus({
statusName: 'init',
statusData: {
Expand All @@ -277,9 +280,9 @@ export function SwapProvider({
amount: from.amount,
fromAddress: address,
from: from.token,
maxSlippage: String(maxSlippage),
to: to.token,
useAggregator,
maxSlippage: maxSlippage.toString(),
});
if (isSwapError(response)) {
setLifeCycleStatus({
Expand Down Expand Up @@ -329,7 +332,6 @@ export function SwapProvider({
from.amount,
from.token,
lifeCycleStatus,
maxSlippage,
sendTransactionAsync,
to.token,
useAggregator,
Expand Down
1 change: 1 addition & 0 deletions src/swap/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const DEFAULT_MAX_SLIPPAGE = 3;
export const GENERAL_SWAP_ERROR_CODE = 'SWAP_ERROR';
export const GENERAL_SWAP_QUOTE_ERROR_CODE = 'SWAP_QUOTE_ERROR';
export const GENERAL_SWAP_BALANCE_ERROR_CODE = 'SWAP_BALANCE_ERROR';
Expand Down