Skip to content

Commit

Permalink
feat: Swap component refactor (coinbase#522)
Browse files Browse the repository at this point in the history
  • Loading branch information
abcrane123 authored Jun 13, 2024
1 parent 6f07c50 commit 7906f46
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 201 deletions.
38 changes: 0 additions & 38 deletions site/docs/pages/swap/swap-amount-input.mdx

This file was deleted.

22 changes: 22 additions & 0 deletions site/docs/pages/swap/swap.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{/* import { Swap, SwapAmountInput, SwapButton } from '../../../../src/swap'; */}
import App from '../App';

# `<Swap />`

### Alert! Component is actively in development. Stay tuned for upcoming releases.

The `Swap` component is a comprehensive interface for users to execute token swaps. It includes two instances of the `SwapAmountInput` component, enabling users to specify the amount of tokens to sell and buy. Additionally, the component features a "Swap" button for initiating the transaction.

## Usage

```tsx [code]
<Swap account={account} onError={onError}>
<SwapAmountInput label="Sell" token={fromToken} type="from" />
<SwapAmountInput label="Buy" token={toToken} type="to" />
<SwapButton onError={onError} onSubmit={onSubmit} />
</Swap>
```

## Props

[`SwapReact`](/swap/types#SwapReact)
18 changes: 12 additions & 6 deletions site/docs/pages/swap/types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ description: Glossary of Types in Swap components & utilities.

```ts
type SwapAmountInputReact = {
amount?: string; // Token amount
disabled?: boolean; // Whether the input is disabled
label: string; // Descriptive label for the input field
setAmount: (amount: string) => void; // Callback function when the amount changes
setToken: () => void; // Callback function when the token selector is clicked
swappableTokens: Token[]; // Tokens available for swap
token?: Token; // Selected token
tokenBalance?: string; // Amount of selected token user owns
type: 'to' | 'from'; // Identifies if component is for toToken or fromToken
};
```

## `SwapReact`

```ts
export type SwapReact = {
account: Account; // Ethereum account
children: ReactNode; // Children components to render
onError?: (error: SwapError) => void; // Callback when swap is unsuccessful
onSuccess?: (swapTransaction: BuildSwapTransaction) => void; // Callback when swap is successful
};
```
4 changes: 2 additions & 2 deletions site/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ export const sidebar = [
text: 'Components',
items: [
{
text: 'SwapAmountInput',
link: '/swap/swap-amount-input',
text: 'Swap',
link: '/swap/swap',
},
],
},
Expand Down
107 changes: 107 additions & 0 deletions src/swap/components/Swap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { useCallback, useMemo, useState } from 'react';
import { cn } from '../../lib/utils';
import { SwapContext } from '../context';
import { getSwapQuote } from '../core/getSwapQuote';
import { isSwapError } from '../utils';
import type { SwapError, SwapReact } from '../types';
import type { Token } from '../../token';

export function Swap({ account, children, onError }: SwapReact) {
const [fromAmount, setFromAmount] = useState('');
const [fromToken, setFromToken] = useState<Token>();
const [toAmount, setToAmount] = useState('');
const [toToken, setToToken] = useState<Token>();

const handleToAmountChange = useCallback(
async (amount: string) => {
setToAmount(amount);
const hasRequiredFields = fromToken && toToken && amount;
if (!hasRequiredFields) {
return;
}
try {
const response = await getSwapQuote({
from: fromToken,
to: toToken,
amount,
amountReference: 'to',
});
if (isSwapError(response)) {
onError?.(response);
return;
}
setFromAmount(response?.fromAmount);
} catch (error) {
onError?.(error as SwapError);
}
},
[fromToken, toToken, setFromAmount, setToAmount],
);

const handleFromAmountChange = useCallback(
async (amount: string) => {
setFromAmount(amount);
const hasRequiredFields = fromToken && toToken && amount;
if (!hasRequiredFields) {
return;
}
try {
const response = await getSwapQuote({
from: fromToken,
to: toToken,
amount,
amountReference: 'from',
});
if (isSwapError(response)) {
onError?.(response);
return;
}
setToAmount(response?.toAmount);
} catch (error) {
onError?.(error as SwapError);
}
},
[fromToken, toToken, setFromAmount, setToAmount],
);

const value = useMemo(() => {
return {
account,
fromAmount,
fromToken,
setFromAmount: handleFromAmountChange,
setFromToken,
setToAmount: handleToAmountChange,
setToToken,
toAmount,
toToken,
};
}, [
account,
fromAmount,
fromToken,
handleFromAmountChange,
handleToAmountChange,
setFromToken,
setToAmount,
setToToken,
toAmount,
toToken,
]);

return (
<SwapContext.Provider value={value}>
<div className="flex w-[400px] flex-col rounded-xl bg-white">
<label
className={cn(
'box-border w-full border-b border-solid p-4 text-base',
'font-semibold leading-6 text-[#030712]',
)}
>
Swap
</label>
{children}
</div>
</SwapContext.Provider>
);
}
Loading

0 comments on commit 7906f46

Please sign in to comment.